[squeak-dev] Direct implementation of shift/reset in Smalltalk
Frank Shearar
frank.shearar at angband.za.org
Thu Apr 21 21:07:10 UTC 2011
On 2011/04/21 19:56, Randal L. Schwartz wrote:
>>>>>> "Frank" == Frank Shearar<frank.shearar at angband.za.org> writes:
>
> Frank> I finished a preliminary version of the shift/reset control operator for
> Frank> creating and using partial continuations:
>
> Frank> http://www.squeaksource.com/Control.html
>
>
> Frank> It's pretty easy to use:
>
> Frank> [ 1 + 2 ] reset => 3
> Frank> [ 1 + [:k | 2 ] shift ] reset => 2 (because k's not used, that part of the
> Frank> stack's discarded)
> Frank> [ 1 + [:k | k value: 2 ] shift ] reset => 3
> Frank> [ 1 + [:k | k value: (k value: 2) ] shift ] reset => 4
> Frank> [ 1 + [:k | cont := k. k value: 0 ] shift ] reset => 1, and cont now holds the
> Frank> continuation, for later (re-)use.
>
> I have no clue what's going on here, and the "best paper" you link has
> greek characters in the second column on the first page.
They do like their Greek characters! (Which paper's throwing the Greek
at you though?)
> Can you explain what's happening in English?
The idea's this: first, we mark the execution stack (with #reset, in
this case). Later, we refer to that mark. In particular, as we send
messages we push MethodContexts and BlockContexts onto the stack.
At any point, we're executing within a particular context. For instance,
when we send #abs to 3 in the below
1 + 2 * 3 abs
we can say that the current context looks like this
1 + 2 * _
or, if you prefer,
[:x | 1 + 2 * x ]
What shift does is it cuts out part of the execution stack - everything
from the current context down to the nearest mark made by reset - and
turns that chunk of stack into a function. So if we have
[ 1 + [:k | k value: 2 ] shift ] reset
then shift creates a unary block that looks like this:
[:x | 1 + x ]
and passes that into the receiver of the shift. Or, to put it another
way, the k in
[:k | k value: 2 ]
is the successor function, so the whole computation becomes
[:k | k value: 2] value: [:x | 1 + x ]
=> [:x | 1 + x] value: 2
=> 3
These examples are obviously very simple and "so what?"-ish. The
Kiselyov and Shan paper I referenced gives a nice in-depth example of
what continuations can do which should be readily recognisable to all of
us: a userland process making system calls: we capture the userland
portion of the stack, store it, hand control over to the system process
which does something or other. When the system's done, it restores the
userland chunk o' stack and the userland process continues computing.
Another excellent intro is Chung Chieh Shan's "Shift to Control"
(http://www.cs.rutgers.edu/~ccshan/recur/recur.pdf). Most of the paper's
*cough* pretty hairy, especially if you're not familiar with the CPS
transform (like me). However, the introduction to the paper is really great.
This idea of "here we are, in some context" is the execution stack
version of a zipper, only instead of walking over a list or a tree,
we're walking over a stack of ContextParts. Hence "a zipper is a
delimited continuation".
Does that help?
frank
More information about the Squeak-dev
mailing list
|