Dynamic scoping
Allen Wirfs-Brock
Allen_Wirfs-Brock at Instantiations.com
Fri Jan 31 23:09:57 UTC 2003
At 11:46 PM 1/30/2003 -0500, Anthony Hannan wrote:
>...
>Allen Wirfs-Brock <Allen_Wirfs-Brock at Instantiations.com> wrote:
> > The topic at hand is dynamic "variable" scoping.
>
>I prefer messages to variables because they are more powerful and
>general. It allows more sophisticated queries, instead of just
>returning a fixed value. I guess I'm just proposing another form of
>exception handling, where exceptions are distinguished by selector
>instead of class, that is why I reused #on:do:.
>
>I even like to think of global variables as messages sent to Smalltalk
>or the class's environment/pool. Like Self, no variables just objects
>and messages.
The reason I quoted "variable" above is because the majority of the recent
discussions and proposals concerning "dynamic scoping" have not been about
scoping actual variable references but about a mechanism and message
protocol for creating a dynamically scoped name/value association context.
Almost all the proposals I have seen use some message protocol to
accomplish this. The only alternative would be to change the Smalltalk
language (and hence compiler's) definition of variable reference such that
they are dynamically rather than lexically scoped. For now, I will simply
assert that this would be a bad idea.
Something you may want to consider is what constitutes the difference
between a language extension and a new language. A good language extension
should add the desired functionality in a way that is both program and
programmer compatible with the existing language. In the absence of it's
use, an extension shouldn't change the behavior of any existing programs
and probably even more importantly, it shouldn't change any programmer's
conceptual understanding of the language. If an extension doesn't have
these characteristics then what you are actually doing is defining a new
language. This may be appropriate in some circumstances but if that is
what you are doing you need to be up front about it. "Self" is a
Smalltalk-like language but it is clearly a different language. If you want
to define a new Smalltalk-like language that is your prerogative, but don't
try to sneak it in as part of a focused functional extension to the
existing language.
You still seem to want to have some sort of commonality between exception
handling and dynamic "variable" scoping. I suspect this is your language
implementor persona getting in the way of your language designer persona.
Clearly, exception handlers use a dynamic scoping mechanism and it has been
demonstrated that dynamically scoped name/value associations (fluids would
be a fine shorthand for that phrase) can be implemented using the Smalltalk
exception mechanism. However, that doesn't mean that exceptions and fluids
are the same thing. I assert that a programmer who needs to establish an
exception handling context is thinking about something very different from
a programmer who needs to establish a fluid binding context. You will
only create confusion, if you try to make them look the same.
Equating exceptions and fluids would also unnecessarily limit your
implementation alternatives. It would not be at all surprising to discover
that the usage patterns of exceptions and fluids are quite different and
call for differing optimization strategies. For example, you might discover
that "shallowing bind" mechanisms are better suited for one and "deep
binding" mechanisms for the other.
Another good language design rule is to optimize the design of a feature
for its most common usage. In this case I'm pretty confident that the most
common usage would be a fixed value binding. While an "active value" (a
block evaluation) is more powerful, it isn't going to be the most common
usage. By requiring a block for the bound value you are complicating the
most common usage and introducing likely error scenarios that would not
exist for fixed values. It's fine to provide an alternative "active value"
form but don't force everybody to use it all the time.
>To satisfy your design principles, we could change the syntax to:
>
>foo
> Context bind: #world toDo: [myWorld] during: [self bar].
>
>bar
> Context world.
>
>Context would be an object that only understands #bind:toDo:during: and
>#doesNotUnderstand:. #doesNotUnderstand: would search the sender chain
>for the first #bind:toDo:during: context for the message's selector and
>execute its toDo: block. I believe this syntax satisfies the design
>principles you mention below.
Aside from the block as the second argument, my main concern is the
#doesNotUnderstand: trickery. Another good Smalltalk design principle is:
don't use #doesNotUnderstand: to create surface syntax. The virtual
machine implementor will have made optimization trade-offs based upon
observed or projected usage of #doesNotUnderstand:. What if the usage
patterns of fluids are very different from those and suggest the need of a
higher level of optimization? In your design, you don't have a local
optimization alternative. You are going to have to tackle optimizing
#doesNotUnderstand:.
So, my preference (using this vocabulary) would be:
foo
Context bind: #world to: myWorld during: [self bar]. "ok to keep
#bind:toDo:during: as an alternative"
bar
Context bindingOf: #world.
If your concern is the verbosity of #bindingOf: I would go with the symbol
based protocol:
foo
#world bindTo: myWorld during: [self bar].
bar
#world binding
I suspect that the verbosity is probably not really a big issue as I
suspect that most of these binding should be encapsulated in other methods.
For example you might expect Morphic class to define:
use: aWorld during: aBlock
^Context bind: #world to: aWorld during: aBlock.
currentWorld
|w|
w := Context bindingOf: #world.
w ~~ nil ifTrue: [^w] ifFalse: [^World]
Allen_Wirfs-Brock at Instantiations.com
More information about the Squeak-dev
mailing list
|