[DOC] Call/CC Re: [Q][Goodie] continuations (was: RE: Tail Call)

Rob Withers rwithers12 at mediaone.net
Sat Feb 2 23:40:36 UTC 2002


Hi Scott,

It took me a little time to figure some of this out, and none of it was due 
to that aweful link you posted!  ;-)   When I search the web for this 
topic, most of what I get back is headache food.

The trick with doing callCC in Squeak, is that we don't have callCC:, like 
your foo example.  Instead we create a continuation around a block and not 
part of a #callCC: expression.   Below is a fixed version of your foo/bar: 
example.

>foo
>^([:continuation  |  self bar: continuation] valueWithCC) +10
>
>bar: continuation
>   continuation value: 15
>   ^16

Another issue is with which Process we create or invoke a 
continuation.  Anthony's #run method sounds perfect for this.  I have 
attached a ContinuationFuture changeset.  Here are some other things I 
learned in writting this.

If you don't fork your call to the creation of a continuation, whatever you 
do afterwards will be executed when you invoke the continuation.  If you do 
an inspect, then each time you invoke the continuation, it will be 
reinspected.  If you happen to invoke the continuation, just after you have 
created it, then without invoking conditionally, you are in an infinite 
loop.  I decided to create continuations from a forked process, that takes 
a second block to do work on the value of the continuation.  I do this fork 
at timingPriority, so I know it gets created before I try to invoke it.

If you don't fork your call to the invocation of the continuation, then the 
process you invoke it with is *gone*.  If this happens to be your UI 
Process, then you are stuck without a UI Process.  This is typically more 
fatal than the first case, especially if you start infitine looping.   I 
believe the priority you fork the invocation of a continuation is the 
priority that the continuation runs at. I default to highIOPriority.

The key to getting thi sto work was you example of error handling of 
objects.  With this example, you bail the first time through the setup 
method (#errorIn:handle:), when the continuation returns nil, as it does 
the first time.  Here was that code:


>Or, a more sophisticated example that handles objects and nested exn
>handlers.
>
>Processor>>error: errorObj
>   self callCC: [:cont | self exnhandler value: errorObj]
>
>Processor>>errorIn: aBlock :handle: aHandleBlock
>   oldhandle _ self exnHandler.
>   err _ self callCC: [:cont | self exnHandler: self
>                         cont nil.]
>   err ifNil: [^nil]
>   self exnHandler: oldhandle.
>   aHandleBlock value: err.

Here is a bare-bones SimpleContinuationFuture, that is inthe attached 
changeset.

Object subclass: #SimpleContinuationFuture
         instanceVariableNames: 'valueContinuation'
         classVariableNames: ''
         poolDictionaries: ''
         category: 'Kernel-Methods'

value: value

         [valueContinuation value: value] forkAt: Processor highIOPriority.
         Processor yield.

whenResolved: aBlock

         | valueResolution initial |
         [initial := true.
         valueResolution := [:cont | valueContinuation := cont. nil] 
valueWithCC.
         initial
                 ifTrue: [initial := false]
                 ifFalse: [aBlock value: valueResolution]] forkAt: 
Processor timingPriority.

example

         | cont |
         cont := SimpleContinuationFuture new
                 whenResolved: [:each | each inspect].
         cont value: 23.

Since I fork both the creation and the evaluation, there are no infinite 
loops or lost UIProcess.  I see know that the 23 that I send to value, is 
set to the value of the continuation, which logically hooks into the 
#whenResolved method, just where the valueWithCC send is.   When you invoke 
the continuation with #value:, that value is the return value of the 
#valueWithCC and so is assigned to the valueResolution variable.

The use of initial is key, since it allows us to setup the continuation, 
without evaluating any of the code in the false block.  It does set the 
initial variable to false, so the next time we invoke it, this value is 
passed to the aBlock argument.

comments interspersed...

At 12:15 PM 2/1/2002, you wrote:
>On Fri, 1 Feb 2002, Withers, Robert wrote:
>
> > Hi Bijan and all,
> >
> > That is a very good analogy, to me, at least at the design level.  However,
> > it's application is confusing, to say the least, especially when you
> > consider it's implications in the squeak environment.  I have a great need
> > to understand it, however, because 'E' uses them to resolve the promises
> > that are created from an eventual send.  Eventual send semantics are my 
> next
> > little project (and I don't mean to just talk about it).
> >
>
>Ok. In the case of a CPS style execution environment, the continuations
>are all explicit arguments.. But, since the real squeak runtime uses a
>stack to represent the continuation thats implicit as the return
>continuation... Normally, in most languages, you can never 'get your
>hands on it'.  Call/CC (which means 'call with current continuation' is
>a way that gets  your hands on that continuation..
>
>Conceptually, CallCC should return a block that, when #value:, immediatly
>jumps back to the continuation. That invocation will not return [*]

This block is a Continuation object in Vassili's changeset.  It represents 
the return continuation from the call to #valueWithCC, right?

<snip>

--

>Now, in implementation terms in something like squeak, thats quite a bit
>uglier, because you can't take a stack of methodcontexts and pretend its
>an anonymous block that takes a single argument. Ergo, to get it, you do
>things like clone the entire methodcontext stack.

Yes, I see now.


>In the other examples we've given, we're not trying to access that 'system
>level' implicit continuation. We're making explicit continuations
>ourselves as blocks we pass them explicitly as arguments. Thats why those
>examples differ from callCC.

Is there a better, faster way to do this ContinuationFuture?


>Continuations this way can be mind-blowing. :)  I'm half reconstructing
>all of this stuff on the fly. Its great! :) I've not gotten this deep in a
>while.

Yes, me too!   I am really impressed with the power of these things.  They 
carry around little frozen stacks that you can restart, and there are 
certainly some interesting things that can be done with them.  I am well on 
my way into implementing References and Promises for an Elib like 
mechanism.  I have a FarRef and a Promise, and the Promise is created from 
a send to the FarRef and is waiting for the future continuation to 
resolve..  Further sends to the Promise, also return promises.  I'll keep 
working on that a little.

Thank you everyone, for the great explorations into these things.

cheers,
Rob
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ContinuationFuture.1.cs.gz
Type: application/octet-stream
Size: 1739 bytes
Desc: not available
Url : http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20020202/108df51d/ContinuationFuture.1.cs.obj


More information about the Squeak-dev mailing list