[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: So, what the heck is a continuation anyway?

Dan Sugalski <dan@sidhe.org> writes:

> That's evil. I really like it. :)

Yes, that's the key understanding here.  Shriram may jump in with some
pretense of continuations' practicality in web applications, where
hitting the back button and re-submitting a form conceptually harmonizes
with re-invoking a continuation.  However, the real use of continuations
is to bend peoples' minds with code like
  ((call/cc call/cc) (call/cc call/cc))

Continuations can look innocent at first.  They can be used as a clean
substitute for labeled goto.  For example, you might say
  (define x (label (my-return) ... ))

(Here, ... represents elided code, in which you can say (my-return val)
anywhere to return a value to be assigned to x.  Just like in
C/Perl/etc., it's handy to use this style so as not to have too-deeply
nested if/else clauses.

Here's more complete example code:

(define (find-neg some-list)
  (label (return)
    (define (return-neg x)
      (if (negative? x)
	  (return x)))
    (for-each return-neg some-list)

In Scheme, for-each normally iterates through every member of a list.
However, by invoking a continuation, we escape out of the for-each as
soon as a negative number is found.

Here's where it gets evil: Note that (return x) in the return-neg
procedure is not yielding return-neg's value.  The procedure named
"return" here always jumps to returning a value where (labels ...) is,
in this case the return value of find-neg.

Here's where it gets really evil: The poor for-each procedure has no
idea what it's getting into.  It thinks return-neg is just some regular
procedure that it will call at each step along the list, and then
continue merrily on its way.  Little does for-each know that return-neg
may wrest execution from it, never to be given back!

As if that wasn't evil enough, here's the secret other Scheme
programmers have been trying to obscure: Sure, technically a
continuation is not a procedure, but the only way continuations can be
represented in Scheme is as a procedure.  In the above code, "return" is
a procedure indistinguishable from any other.  In other words, in the
context of Scheme programming, you're 100% correct that a continuation
is "a function call".  That's what opens up the doors to doing all
sorts of things with continuations that nice people wouldn't do.

P.S. My example is not standard Scheme.  I used the following macro to
make call/cc more palatable:

(define-syntax label
  (syntax-rules ()
    ((label (foo) expr ...)
     (call/cc (lambda (foo) expr ...)))))