[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