DynamicBindings

BindingsAccessor
I enable access to the current dynamic bindings. My class initialization adds my default instance to the system dictionary as #Bindings. This enables access to the active runtime environment using syntax like the following:
Bindings at: #AProcessLocalVariable put: aValue
Bindings at: #AProcessLocalVariable
I also allow for the creation of new DynamicBindings as follows:
Bindings new. "Creates a new empty DynamicBindings instance"
Bindings clamp: []. "Creates new bindings that inherit from the current bindings"
associationsDo:
at:
Primitive. Assumes receiver is indexable. Answer the value of an
indexable element in the receiver. Fail if the argument index is not an
Integer or is out of bounds. Essential. See Object documentation
whatIsAPrimitive.
at:ifAbsent:
at:ifAbsentPut:
at:ifPresent:
at:put:
Primitive. Assumes receiver is indexable. Store the argument value in
the indexable element of the receiver indicated by index. Fail if the
index is not an Integer or is out of bounds. Or fail if the value is not of
the right type for this kind of collection. Answer the value that was
stored. Essential. See Object documentation whatIsAPrimitive.
bind:to:
bindingFor:ifNotBound:
clamp:
Ensures that any environment modifications that occur during the evaluation of aBlock will happen in an isolated environment and that the original environment will be restored after evaluation completes. Concurrent changes in the parent bindings are visible while executing aBlock.
default
do:
explore
This is just a handy way to allow you to directly explore the
global symbol 'Bindings'
get
includesKey:
initialize
Subclasses should redefine this method to perform initializations on instance creation
inspect
This is just a handy way to allow you to directly inspect the
global symbol 'Bindings'
isBound:
isolate
Isolates the bindings of the active context such that changes to the
active bindings are not visible anywhere except that portion of the active
stack for which the active bindings are assigned.
isolate:
Ensures that any environment modifications that occur during the evaluation of aBlock will happen in an isolated environment and that the original environment will be restored after evaluation completes. Concurrent changes in the parent bindings are visible while executing aBlock.
keys
new
Creates a new runtime environment that is empty.
newChild
Creates a new runtime environment that inherits from the current environment.
parent
pvtBindings
pvtContext
removeKey:
removeKey:ifAbsent:
root
values
BindingsLocator
I am used to find the runtime environment of a given ContextPart. If no suitable handler is found, then I will install dummy contexts at the top of the stack that include a handler that returns the default bindings for the system (ie. "DynamicBindings root").
defaultAction
Finds the root context for the active stack, then installs a new
handler context that will answer the context containing the root
environment. This method depends on the implementation of
#valueWithBindings:.
DynamicBindingTests
I test the DynamicBindings subsystem.
testAccessor1
testAccessor2
testFork
In this test, we examine the isolation behavior when forking a process. We are
checking to ensure that changes made to the environment in the forked process do
not impact the calling process. We also check to make sure that the forked process
gets the current environment from the calling process at the time of the fork.
testSharedRuntimeEnvironment
In this test, we examine the isolation behavior when forking processes and when those
processes share a runtime environment. We are checking to ensure that both forked
processes see the changes made to the shared runtime environment even when their
own environments are isoldated from each other by a modification.
DynamicBindings
DynamicBindings for Squeak
by Stephen Pair <spair@pairhome.net>
I am a dictionary with an added property that I can inherit associations from other instances (via the parent inst var). I am intended to be used as a set of variable bindings that can be local to a process or shared among a group of processes. I am an abstract class and should never be instantiated.
Here's how DynamicBindings work:
You may create a hierarchy of DynamicBindings and directly manipulate the keys and values of those instances (just as you would any dictionary). There is a root DynamicBindings that you may use to create new children if you like (but you can also create entirely separate hierarchies). You can access this root with "DynamicBindings root". To force a process to use one of your sets of DynamicBindings, you write code as follows (note, I only use 'PATH' to highlight the similarity with normal OS environment variables):
myBindings _ DynamicBindings root newChild.
myBindings at: #PATH put: '/bin:/usr/bin'.
myBindings bindDuring:
[Bindings at: #PATH. "-> '/bin:/usr/bin'"
Bindings at: #PATH put: ('/usr/local/bin:', (Bindings at: #PATH)).
Bindings at: #PATH]. "-> '/usr/local/bin:/bin:/usr/bin'"
For convenience, two methods have been added to Object that enable more direct access to bindings. The following exampel is equivalent to the first:
myBindings _ DynamicBindings root newChild.
myBindings at: #PATH put: '/bin:/usr/bin'.
myBindings bindDuring:
[#PATH binding. "-> '/bin:/usr/bin'"
#PATH binding: ('/usr/local/bin:', (#PATH binding)).
#PATH binding]. "-> '/usr/local/bin:/bin:/usr/bin'"
The first line creates a new set of bindings that is a child of the root bindings. Any variables set in the root bindings are also visible in this new child environment.
The second line sets the dynamic variable "PATH".
The third line uses the evaluates the argument block in the context of our new set of bindings.
The fourth line gets the value for the variable "PATH" (which we just set).
The fifth line in the above example modifies the environment variable "PATH", but only for the duration that the enclosing block is active.
Here is another example:
#PATH binding: '/bin'.
Bindings clamp:
[#PATH binding: '/usr/bin'.
#PATH binding]. "-> '/usr/bin'"
#PATH binding. "-> '/bin'"
This example shows the use of the #clamp: method to isolate a our dynamic bindings only for the duration of a block. After the block finishes execution, the original set of bindings is restored.
Scoping semantics:
A dynamic variables' scope is defined at runtime by an enclosing activation context. To locate the active lexicon of dynamic variables, the system follows the activation stack until a context is encountered that defines the active DynamicBindings (this lookup is implemented using the exception handling system). Any changes to the active DynamicBindings are visible in all activation contexts where that set of DynamicBindings are being used (which may include contexts in more than one activation stack).
A bit about how it's implemented:
The bindings for a given method activation context are located using the exception handling mechanism (see BindingsLocator). If a given stack does not have a handler that answers a set of bindings, then dummy contexts will be added to the top of the stack that will answer the global root set of bindings.
Unlike other implementations, DynamicBindings do not use the activation stack to define the hierarchy of bindings. Instances of DynamicBindings have their own parent instance variable and will locate enclosing variable scopes by following the chain of parents (*not* by looking for enclosing handlers of BindingsLocator). Using this design, we are able to accomodate a broader range of usage scenarios.
If you need to isolate the bindings of a given context stack such that future changes in the bindings are not visible to users of the current set of dynamic bindings (ie. if you use continuations), you can send the message #isolate to the BindingsAccessor (ie. use "Bindings isolate").
at:ifAbsent:
Answer the value associated with the key or, if key isn't found,
answer the result of evaluating aBlock.
atThisLevel:ifAbsent:
bind:to:
bindDuring:
bindingFor:
bindingFor:ifNotBound:
clear
example
fork:
forkClamped:
forkIsolated:
initialize
Subclasses should redefine this method to perform initializations on instance creation
isBound:
level
newChild
newFrom:
parent
parent:
root
DynamicBindingsInfo
DynamicBindings for Squeak
by Stephen Pair <stephen@pairhome.net>
===== Introduction =====
You may create a hierarchy of DynamicBindings and directly manipulate the keys and values of those instances (just as you would any dictionary). There is a root DynamicBindings that you may use to create new children if you like (but you can also create entirely separate hierarchies). You can access this root with "DynamicBindings root". To force a process to use one of your sets of DynamicBindings, you write code as follows (note, I only use 'PATH' to highlight the similarity with normal OS environment variables):
myBindings _ DynamicBindings root newChild.
myBindings at: #PATH put: '/bin:/usr/bin'.
myBindings bindDuring:
[Bindings at: #PATH. "-> '/bin:/usr/bin'"
Bindings at: #PATH put: ('/usr/local/bin:', (Bindings at: #PATH)).
Bindings at: #PATH]. "-> '/usr/local/bin:/bin:/usr/bin'"
For convenience, two methods have been added to Object that enable more direct access to bindings. The following exampel is equivalent to the first:
myBindings _ DynamicBindings root newChild.
myBindings at: #PATH put: '/bin:/usr/bin'.
myBindings bindDuring:
[#PATH binding. "-> '/bin:/usr/bin'"
#PATH binding: ('/usr/local/bin:', (#PATH binding)).
#PATH binding]. "-> '/usr/local/bin:/bin:/usr/bin'"
The first line creates a new set of bindings that is a child of the root bindings. Any variables set in the root bindings are also visible in this new child environment.
The second line sets the dynamic variable "PATH".
The third line uses the evaluates the argument block in the context of our new set of bindings.
The fourth line gets the value for the variable "PATH" (which we just set).
The fifth line in the above example modifies the environment variable "PATH", but only for the duration that the enclosing block is active.
Here is another example:
#PATH binding: '/bin'.
Bindings clamp:
[#PATH binding: '/usr/bin'.
#PATH binding]. "-> '/usr/bin'"
#PATH binding. "-> '/bin'"
This example shows the use of the #clamp: method to isolate a our dynamic bindings only for the duration of a block. After the block finishes execution, the original set of bindings is restored.
Scoping semantics:
A dynamic variables' scope is defined at runtime by an enclosing activation context. To locate the active lexicon of dynamic variables, the system follows the activation stack until a context is encountered that defines the active DynamicBindings (this lookup is implemented using the exception handling system). Any changes to the active DynamicBindings are visible in all activation contexts where that set of DynamicBindings are being used (which may include contexts in more than one activation stack).
A bit about how it's implemented:
The bindings for a given method activation context are located using the exception handling mechanism (see BindingsLocator). If a given stack does not have a handler that answers a set of bindings, then dummy contexts will be added to the top of the stack that will answer the global root set of bindings.
Unlike other implementations, DynamicBindings do not use the activation stack to define the hierarchy of bindings. Instances of DynamicBindings have their own parent instance variable and will locate enclosing variable scopes by following the chain of parents (*not* by looking for enclosing handlers of BindingsLocator). Using this design, we are able to accomodate a broader range of usage scenarios.
If you need to isolate the bindings of a given context stack such that future changes in the bindings are not visible to users of the current set of dynamic bindings (ie. if you use continuations), you can send the message #isolate to the BindingsAccessor (ie. use "Bindings isolate").
===== Release History =====
Version 1.1:
- Packaged using KomPackaging (Squeak version only)
- Made Object>>binding: answer its argument
- Cleaned up a few obsolete methods from the old RuntimeEnvironments implementation
Version 1.0:
- Initial release
preambleText
Executed first to load the package
releaseName