guile-devel
[Top][All Lists]
Advanced

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

doco continuations


From: Kevin Ryde
Subject: doco continuations
Date: Tue, 08 Jul 2003 08:17:55 +1000
User-agent: Gnus/5.090019 (Oort Gnus v0.19) Emacs/21.2 (gnu/linux)

This is a proposed revision for the continuations node in the manual.
It tries to illustrate the concept, though at a reference manual
level, not in as much detail as a tutorial might.

At the end I mention threads for coroutines.  Would that be the best
alternative to continuations?  A thread plus a condition variable for
signalling it to wake up, or something along those lines.



Continuations
=============

A "continuation" is the code that will execute when a given function or
expression returns.  For example, consider

     (define (foo)
       (display "hello\n")
       (display (bar)) (newline)
       (exit))

   The continuation from the call to `bar' comprises a `display' of the
value returned, a `newline' and an `exit'.  This can be expressed as a
function of one argument.

     (lambda (r)
       (display r) (newline)
       (exit))

   In Scheme, continuations are represented as special procedures just
like this.  When a continuation is called it abandons the current
program location and jumps directly to that represented by the
continuation.

   A continuation is a sort of dynamic label, capturing at run-time a
point in program execution, including all the nested calls that have
lead to it (or rather the code that will execute when those calls
return).

   Continuations are created with the following functions.

 - Scheme Procedure: call-with-current-continuation proc
 - Scheme Procedure: call/cc proc
     Capture the current continuation and call `(PROC CONT)' with it.
     The return value is the value returned by PROC, or when `(CONT
     VALUE)' is later invoked, the return is the VALUE passed.

     Normally CONT should be called with one argument, but when the
     location resumed is expecting multiple values (*note Multiple
     Values::) then they should be passed as multiple arguments, for
     instance `(CONT X Y Z)'.

     CONT may only be used from the dynamic root in which it was
     created (*note Dynamic Roots::), and in a multi-threaded program
     only from the thread in which it was created, since each thread is
     a separate dynamic root.

     The call to PROC is not part of the continuation captured, it runs
     only when the continuation is created.  Often a program will want
     to store CONT somewhere for later use, this can be done in PROC.

     The `call' in the name `call-with-current-continuation' refers to
     the way a call to PROC gives the newly created continuation.  It's
     not related to the fact a call is used later to invoke that
     continuation.

     `call/cc' is an alias for `call-with-current-continuation'.  This
     is in common use since the latter is rather long.


Here is a simple example,

     (define kont #f)
     (format #t "the return is ~a\n"
             (call/cc (lambda (k)
                        (set! kont k)
                        1)))
     => the return is 1
     
     (kont 2)
     => the return is 2

   `call/cc' captures a continuation in which the value returned is
going to be displayed by `format'.  The `lambda' procedure stores this
in `kont' and gives an initial return `1' which is displayed.  The
later invocation of `kont' resumes the captured point, but this time
returning `2', which is displayed.

   When Guile is run interactively, a call to `format' like this has an
implicit return back to the read-eval-print loop.  `call/cc' captures
that like any other return, which is why interactively `kont' will come
back to read more input.


   C programmers may note that `call/cc' is like `setjmp' in the way it
records at runtime a point in program execution.  A call to a
continuation is like a `longjmp' in that it abandons the present
location and goes to the recorded one.  Like `longjmp', the value
passed to the continuation is the value returned by `call/cc' on
resuming there.  However `longjmp' can only go up the program stack,
but the continuation mechanism can go anywhere.

   When a continuation is invoked, `call/cc' and subsequent code
effectively "returns" a second time.  It can be confusing to imagine a
function returning more times than it was called.  It may help instead
to think of it being stealthily re-entered and then program flow going
on as normal.

   `dynamic-wind' (*note Dynamic Wind::) can be used to ensure setup
and cleanup code is run when a program locus is resumed or abandoned
through the continuation mechanism.  For instance locking and unlocking
database records in use, or similar.


   Continuations are a powerful mechanism, and can be used to implement
almost any sort of control structure, such as loops, coroutines, or
exception handlers.

   However the implementation of continuations in Guile is not as
efficient as one might hope, because Guile is designed to cooperate
with programs written in other languages, such as C, which do not know
about continuations.  Basically continuations are captured by a block
copy of the stack, and resumed by copying back.

   For this reason, continuations should generally be used only when
there is no other simple way to achieve the desired result, or when the
elegance of the continuation mechanism outweighs the need for
performance.

   Escapes upwards from loops or nested functions are generally best
handled with exceptions (*note Exceptions::).  Coroutines can be
efficiently implemented with cooperating threads (a thread holds a full
program stack but doesn't copy it around the way continuations do).




reply via email to

[Prev in Thread] Current Thread [Next in Thread]