Dynamic scoping (was: Proposal: Squeak-E = Squeak x Kernel-E)

Stephen Pair spair at acm.org
Tue Jan 28 01:40:00 UTC 2003


Avi Bryant wrote:
> On Mon, 27 Jan 2003, Stephen Pair wrote:
> 
> > So remind me again why it's desirable to reuse the context 
> stack for 
> > runtime environments (and ultimately exceptions for that 
> matter)?  I'm 
> > starting to doubt that it is.  Yes, there are some things which are 
> > simplified...but I think I might be squeezing myself into a 
> straight 
> > jacket because it happens to feel warm and cuddly.
> 
> I don't know about "runtime environments", because I don't 
> know what your goals are.  But dynamic scope should be 
> implemented with the context stack because that's how dynamic 
> scope is defined.  

Perhaps someone at some point defined it that way, but that doesn't
necessarily mean it's the best definition.

> One thing working with continuations and 
> other non-standard control flow techniques does is force you 
> to be clear about what kind of semantics you need for what. 

Agreed.
 
> I have a definite use for a scoping mechanism wherein a 
> binding's value is resolved by looking up the execution 
> stack.  Yes, this could be implemented in some way other than 
> looking through the sender chain, but then you have to be 
> careful that whenever somebody manipulates the stack, you 
> correspondingly adjust your bindings - and it's just too easy 
> for this to go out of sync.  

But that's only in the implementation where the dynamic bindings are
attached to the process...if they were attached to, but distinct from
the context stack, then I think you could get the added flexibility and
the better design of not having the process hold the dynamic bindings
(and this is where I'm now going with RE).  Lookup would use an
exception to discover the nearest environment, and from there it would
lookup a separate stack of environments.  Clamping would simply find the
nearest environment and create a new isolation.  A forked process would
still need to have an enclosing handler that linked it to the current
environment of the calling context.  Stacks that have no context that
defines an environment will use an image-wide default environment.

Here's another test case...what would you expect the following to output
(assuming that dynamic context were handled across forks)?

----
| s |
s := Semaphore new.
Env at: #test put: '1'.
[
	Transacript show: (Env at: #test); space.
	s signal.
	s wait.
	Transacript show: (Env at: #test); space.	
] fork.

s wait.
Env at: #test put: '2'.
s signal.
----

1 1

or:

1 2

Since there is no explicit isolation (like the use of a #clamp: method),
I would expect them to share an environment and thus output "1 2 "
(note: RE currently outputs "1 1 " because I force the environments to
be isolated when forking, but I now think that's incorrect).

> If exceptions, for example, were 
> implemented in some way other than by walking the stack, I 
> would have to muck about with the exception system every time 
> I invoked a Continuation...

But think about what it would mean if you had a completely different
mechanism for branching off processing (other than the standard
fork)...think about an asynchronous or eventual sending scenario where
your caller may be long expired (and your sender nilled) by the time you
actually need to lookup an exception handler or some dynamic binding.
The same thing happens when forking a process, it's just that it's done
so rarely in today's system that it's easy to gloss over that issue.  If
you could maintain a caller chain that's maintained even after a context
returns, then the current exception handling might work under such
circumstances, but in today's system, it completely falls apart.
 
> Now, there are other times that I need other semantics, but 
> for those times I don't use dynamic scope.
> 
> > I'll have to read up an "amb"...but trippy should not be goal.  ;)
> 
> Amb was done for fun, and as a mind-bending demo of 
> continuations.  I've never actually used it, although I 
> imagine it might be quite useful for parsers and similar 
> problems.  The simplest example is probably something
> like:
> 
> |amb x y|
> amb := Amb new.
> x := amb oneOf: (1 to: 10).
> y := amb oneOf: (1 to: 10).
> amb assert: (x * y) = 42.
> Transcript cr; show: {x. y}.
> 
> This will print
> 
> #(6 7)
> 
> to the Transcript.
> 
> > BTW, the fact that continuations re-use processes threw me 
> for a loop 
> > at first.  When I was doing the stuff for Swiki.net, I 
> didn't re-use 
> > processes, I just created new ones...it seemed a lot easier 
> to follow 
> > the control flow.
> >
> > For example, when you evaluate a continuation, why not have the 
> > continuation happen in a separate process?
> 
> I think the implementation would be a lot more complicated, 
> and I don't see how it would be useful.  If you want the 
> continuation to happen in a separate process, just fork 
> before you evaluate it.

Yes, perhaps it is too much to assume that you want it evaluated in
another process...but I'd say that in most cases you *should* put it
into another process (if for no other reason than to preserve the sanity
of people trying to read your code).  Perhaps the standard #value should
do the fork for you and have a bit more explicit #jumpInto or something
similar that would give you some indication that you're about to jumple
the active context stack.  But then again, maybe Semaphores are just too
ingrained in psyche and I just need to unlearn them a bit.

As I see it, the evaluation of a continuation is just like signaling a
semaphore except that you control which stack gets activated and you
have this added side effect of immediately terminating the current
process (with no unwinding of contexts mind you).  I'm just not
convinced that this is a good thing.

> > I've been thinking of ways to clean up Continuation a bit 
> such that it 
> > eliminates such freakish behavior.  I think this might have the 
> > pleasurable side effect of making Seaside's use of 
> continuations a bit 
> > easier to follow.
> 
> I doubt it ;).

Maybe not, but I've seen it done both ways.  ;)

- Stephen



More information about the Squeak-dev mailing list