[Squeak-e] Are Handlers Dynamically Scoped? (was: Comments on Lex's "Object as Capabilities in Squeak")

Allen Wirfs-Brock Allen_Wirfs-Brock at acm.org
Sun Feb 2 20:23:53 CET 2003

At 01:48 PM 2/2/2003 -0800, Mark S. Miller wrote:
>At 12:28 PM 2/2/2003 Sunday, Allen Wirfs-Brock wrote:
> >  In the Smalltalk  model the
> >handler is executed before the stack is unwound. This allows resumable
> >handles to be trivially  implemented.
>Does it have any other virtue? If we didn't have resumable handlers, would
>there be any remaining reason to prefer this? Note that the issue isn't when
>they're executed, but when they're looked up. Of course, they can't be
>executed until they're looked up.

The obvious advantage is that the complete state of the computation  is 
still available during the evaluation of the handler. This is clearly 
essential  for resumption and is certainly useful for debugging. I can 
speculate that there are non-debugging situations where it is useful for a 
non-resuming handler to have  the computation state at the exception point 
available (or at least  preserved) but I don't have a canonical example at 
my finger tips.  There is quite a  few years of  experience with 
this  mechanism in the broader Smalltalk community.  Perhaps somebody out 
there can provide a good example.

Stepping  back, this appears  to me to be a classic early/late binding 
trade-off. The Java model binds the decision to  discard the computation to 
an early stage in the exception processing sequence. The Smalltalk  model 
defers that decision to a much  latter point. As is usually  the case, late 
binding provides more flexibility but carries a price.

The Smalltalk model with resumable exceptions certainly feels more powerful 
in a way that is consistent with the more dynamic nature of Smalltalk. 
However, I don't know that I'm prepared to argue that resumable exceptions 
are essential (rather than just useful).

> >I'm relatively confident that your CPS  model could  be extended to
> >accommodate the Smalltalk exception model  although it  might be some  work
> >to do so.
>The only way I can imagine reveals that this semantics is indeed a case of
>dynamic scoping. The way I imagine:
>A continuation could have an additional method available,
>"getHandler: exceptionTypeOrSomething" that either already knows a handler
>for that exceptionTypeOrSomething, or asks its continuation. This
>non-destructive looking up the stack is simply a deeply bound implementation
>model of dynamic scoping. (Alternatively, the continuation could instead have
>a non-destructive "handle: exception" method which gets delegated back, but
>it amounts to the same thing.)

That is  essentially one (well, actually two) way(s)  to implement it. In 
fact, the ANSI Smalltalk  exception mechanism doesn't need to have  any 
particularly unique primitive support.  All you need are closures, a 
primitive unwind  mechanism, single-use continuations ([^]),  and  a single 
thread-local variable. The original Digitalk implementation used a shallow 
binding technique. A thread local pointed to a liked  list of active 
handler states.  Establishing a protected  region adds a new element to the 
head of the list. Exiting the protected region delete the head of the list. 
Unwind protection is  used to ensure the integrity  of the list. Each list 
element records the exception to be handled, the closure for the handler, 
and the continuation used to terminate the handler. Signaling an 
exception  is a matter of searching the list for an entry that handles the 
exception and resetting the list head before evaluating the closure. I 
personally  prefer this implementation technique over deep binding 
techniques that  probe the call stack to find handlers as it doesn't 
require reification of the stack.

A reasonably high fidelity approximation of the Smalltalk exception system 
can be implemented in Java. Java has unwind protection as well as thread 
locals  and Java  exceptions can be used  as the continuation 
mechanism.  Java don't have real closures but anonymous inner classes can 
be used as an approximation.

If you accept that Smalltak exceptions can be implemented using the above 
primitives then I'm not sure that you even have to explicitly account for 
exceptions in your formal model (assuming that you do model the primitives).

>The key thing about the Java alternative, unwinding to the handler, is that
>the continuation is only ever invoked destructively, and is otherwise
>opaque. So by the time the handler is invoked, it's a handler associated
>with the immediate continuation, and not one retrieved from further back on
>the stack.
>So even without resumption, if earlier handlers get invoked before later
>unwind blocks, then I'd agree with Anthony that Smalltalk's exceptions are
>an instance of dynamic scoping. This isn't to say that it's a bad idea. But
>it does leave us with the following hypotheses:

Most of the legitimate uses of resumable exceptions that I know of do, 
indeed, seem to be specialized examples of dynamic scoping.

>1) Resumable handlers are bad.
>2) Dynamically scoped resumable handlers (as in Smalltalk) are good, leaving
>us with at least one case where dynamic scoping is a good idea. If it's a
>good idea here, there are probably other cases as well.
>3) Resumable handlers are good, but a resumable handler shouldn't be looked
>up by dynamic scoping. (Note: Joule has lexical resumable handlers called
>4) Terminating handler lookup should happen during unwind, like Java.
>5) Terminating handler lookup should happen prior to unwind, as in
>Smalltalk, making this handler lookup arguable another case of dynamic 
>(In order to make this point separate from #2, let's say "should" even in
>the absence of the need to support #2.)
>6) Terminating handler lookup should happen prior to unwind, but not by
>looking up the stack. (Presumably, the alternative would be lexical. I know
>of no systems that do this.)
>Smalltalk: #2, #5.
>Java & E: #1, #4.

In the case of Java/C++, I'm  not sure that #1 is the motivation for their 
design. I believe that the Smalltalk style of handler would be difficult to 
implement (or of very little utility) without first-class closures.

>Joule: #3. (Joule has no stack, and so can't have any conventional notion of
>                 termination.)
>Does this seem like a useful framework for exploring the issue?


>What are some arguments for #2 or #5? I'm prepared to argue for #1, #3, 
>and #4.

To some degree I've touched upon #5 issues above. I willing to make  a case 
for #2 but it will have to be in another message. I believe I share  with 
you the position that static (lexical) scoping is usually preferable to 
dynamic  scoping. However, I am interested in hearing why you may think 
that dynamic scoping is never useful.

Allen_Wirfs-Brock at Instantiations.com

More information about the Squeak-e mailing list