Continuations; was RE: Multiple Returns, was Re: Common Lisp style macros in Smalltalk?

demiourgos at smalltalk.org demiourgos at smalltalk.org
Sun Jan 30 17:54:12 UTC 2000


On Thu, 27 January 2000, "Vassili Bykov" wrote:


Excellent summary Vassili!  Sorry I missed it before.  --jtg

> 
> > From: Jarvis, Robert P. (Contingent) [mailto:Jarvisb at timken.com]
> >
> > OK, I'll exhibit my ignorance :-).  What are continuations, and what are
> > they useful for?  Same questions for coroutines.
> 
> Continuations is a big topic.  They are pretty mind-bending, so no
> explanation can replace actual use of them.  I am attaching my continuations
> (still work in 2.7) for playing with.
> 
> Continuations is an abstraction for non-local control transfer used (among
> some other languages) in  Scheme as the foundation for all the other
> non-local control tricks.  Things like non-timeslicing  multitasking,
> exceptions, catch/throw can all be implemented using continuations.
> 
> First of all, to get matters straight, what one could implement using
> continuations could be  implemented by "regular" Smalltalk means or is
> already available, as objects and blocks, exceptions and processes.  Within
> Smalltalk, using those would of course be a better solution.  This is what I
> meant saying that continuations are far out of Smalltalk thinking.  I still
> find it neat that they can be implemented in Squeak at the image level.
> (You can almost, but not quite, do that in VW--or maybe YOU can).
> 
> Here is what continuations are.  Suppose we have a piece of code that reads
> "quux := aFoo bar".  While the  #bar message send is in progress, we can say
> that the piece of code "quux := ..." expects the value returned by "aFoo
> bar" to substitute it in place of the ellipsis.  We can express this
> expectation as a  block "[:a | quux := a]".  We can imagine that once "aFoo
> bar" completes, the execution continues by invoking that block, with the
> result returned from the #bar message as the argument.  This one-argument
> block is called the continuation of the message send--because it describes
> how execution continues after the message send returns.
> 
> As far as most languages are concerned, continuations are only a theoretical
> concept.  Scheme, on the other hand, makes them first class.  It provides a
> function call-with-current-continuation, often also available as call/cc,
> expecting a one-argument function.  It then calls that function passing the
> continuation of the (call/cc ...) as the argument.  The continuation behaves
> as a one-argument function; calling it has an effect of returning from the
> (call/cc ...) form.  If you feel OK about reading some Scheme, here is a
> stupid but simple example
> 
>   (define (foo n)
>     (call/cc
>       (lambda (cont)
>         (if (zero? n) (cont 'zero))
>         'non-zero)))
> 
> Here, the continuation is used for non-local exit.  A similar thing in
> Smalltalk, shown on c.l.s a couple of times by Eliot Miranda and myself
> would be
> 
>   Block{Context,Closure,whatever}>>valueWithExit
>     ^self value: [:result | ^result]
> 
>   MyClass>>foo: n
>     | temp |
>     temp :=
>       [:exit |
>       n = 0 ifTrue: [exit value: #zero].
>       #non-zero] valueWithExit
> 
> (without the #valueWithExit thing, you could not have #zero assigned to
> "temp", ^ would just leave the method).
> 
> The funky part is, you can hold onto a continuation object and call it
> multiple times, thus causing one function call return multiple times along
> the same call chain.  For example, in Scheme after you evaluate
> 
>   (begin
>     (call/cc
>        (lambda (cont)
>          (set! print-again cont)))
>     (display "Hello again"))
> 
> you could many times evaluate
> 
>   (print-again 'bogus)
> 
> and each time it would print "Hello again", because control "escapes" into
> the continuation and returns along its sender chain--that is the call/cc
> will return (with 'bogus as the value which is ignored) and will then move
> on to the next form.
> 
> See a test method at the class side of Continuation class in the attached
> change set.  In this implementation, Block methods
> #valueWithCurrentContinuation and #valueWithCC are the analogs of
> call-with-current-continuation and call/cc.
> 
> Alan (Knight) already mentioned that you can mimic this stuff by explicitly
> passing blocks around.  This is true by definition--continuations behave
> *almost* like functions of one argument (incidentally, allowing them to have
> may arguments yields a language with multiple return values).  What all the
> fuss is about then?   Theoretically, execution of any program can be viewed
> in terms of continuations.  In languages with first class continuations
> (Scheme or Squeak with the attached file-in), you can "lift" a contunuation
> as a "real" object off a syntactically "normal" program.   In a language
> without first-class continuations, but with closures, you can manually chop
> and slice your program into continuations expressed as blocks.
> 
> How about this summary of how continuations are different from a bunch of
> blocks passed all over:
> 
>     Continuations allow to introduce non-standard evaluation rules
>     in code that uses standard syntax.
> 
> It is getting late, so I'll wrap up here. (I am not sure anyone will read
> this far anyway. :-)  Just a few interesting points before the end.
> 
> A continuation may look just like a function of one argument (a one-argument
> block in the Smalltalk version), but the catch is it has weird call
> semantics.  In the "print-again" example, evaluating
> 
>   (begin
>     (print-again)
>     (display "and goodbye"))
> 
> will never print "goodbye" because calling a continuation, speaking in
> technical terms, replaces the call stack so (print-again) will never return
> to the caller.  You can see the gory details in the code.
> 
> Why does call/cc have the confusing form of accepting a function that will
> receive the actual continuation?  Could we just have something like get/cc,
> a no-argument function that returns its own continuation?  get/cc is less
> general.  It can be implemented using call/cc, in a very cool way:
> 
>   (define (get/cc)
>     (call/cc call/cc))
> 
> (proving that it works is an exercise to the reader).  There are a couple of
> pragmatic considerations as well.
> 
> Finally, people have been known to insist that the correct implementation of
> call/cc is:
> 
>   (define (call/cc foo)
>     (display "cc: Segmentation fault. Core dumped."))
> 
> A good book discussing all that is Lisp in Small Pieces by Christian
> Queinnec.
> 
> Cheers,
> 
> --Vassili

______________________________________________________
Jan Theodore Galkowski      www.smalltalk.org/ 
    http://www.scguild.com/usr/1707I.html 
demiourgos at smalltalk.org  www.marssociety.org/
**********************************************
PGP Key Fingerprint: 2757 F86D AA51 677D 38D7  
                     964B 9A8D 7852 A494 3790
**********************************************
Get my Public Key from my home page at:
   http://home.stny.rr.com/algebraist/
**********************************************

______________________________________________

Get free e-mail at http://www.britannica.com





More information about the Squeak-dev mailing list