mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-23 04:50:28 +02:00
(Continuations): Rewrite with more detail.
This commit is contained in:
parent
382053e987
commit
807a30f1db
1 changed files with 132 additions and 41 deletions
|
@ -249,57 +249,148 @@ times.
|
||||||
|
|
||||||
@node Continuations
|
@node Continuations
|
||||||
@section Continuations
|
@section Continuations
|
||||||
|
@cindex continuations
|
||||||
|
|
||||||
@cindex call/cc
|
A ``continuation'' is the code that will execute when a given function
|
||||||
@cindex call-with-current-continuation
|
or expression returns. For example, consider
|
||||||
The ability to explicitly capture continuations using
|
|
||||||
@code{call-with-current-continuation} (also often called @code{call/cc}
|
|
||||||
for short), and to invoke such continuations later any number of times,
|
|
||||||
and from any other point in a program, provides maybe the most powerful
|
|
||||||
control structure known. All other control structures, such as loops
|
|
||||||
and coroutines, can be emulated using continuations.
|
|
||||||
|
|
||||||
@c NJFIXME - need a little something here about what continuations are
|
@example
|
||||||
@c and what they do for you.
|
(define (foo)
|
||||||
|
(display "hello\n")
|
||||||
|
(display (bar)) (newline)
|
||||||
|
(exit))
|
||||||
|
@end example
|
||||||
|
|
||||||
The implementation of continuations in Guile is not as efficient as one
|
The continuation from the call to @code{bar} comprises a
|
||||||
might hope, because it is constrained by the fact that Guile is designed
|
@code{display} of the value returned, a @code{newline} and an
|
||||||
to cooperate with programs written in other languages, such as C, which
|
@code{exit}. This can be expressed as a function of one argument.
|
||||||
do not know about continuations. So continuations should be used when
|
|
||||||
there is no other simple way of achieving the desired behaviour, or
|
@example
|
||||||
where the advantages of the elegant continuation mechanism outweigh the
|
(lambda (r)
|
||||||
need for optimum performance. If you find yourself using @code{call/cc}
|
(display r) (newline)
|
||||||
for escape procedures and your program is running too slow, you might
|
(exit))
|
||||||
want to use exceptions (@pxref{Exceptions}) instead.
|
@end example
|
||||||
|
|
||||||
|
In Scheme, continuations are represented as special procedures just
|
||||||
|
like this. The special property is that when a continuation is called
|
||||||
|
it abandons the current program location and jumps directly to that
|
||||||
|
represented by the continuation.
|
||||||
|
|
||||||
|
A continuation is like a 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.
|
||||||
|
|
||||||
@rnindex call-with-current-continuation
|
|
||||||
@deffn {Scheme Procedure} call-with-current-continuation proc
|
@deffn {Scheme Procedure} call-with-current-continuation proc
|
||||||
Capture the current continuation and call @var{proc} with the captured
|
@deffnx {Scheme Procedure} call/cc proc
|
||||||
continuation as the single argument. This continuation can then be
|
@rnindex call-with-current-continuation
|
||||||
called with arbitrarily many arguments. Such a call will work like a
|
Capture the current continuation and call @code{(@var{proc}
|
||||||
goto to the invocation location of
|
@var{cont})} with it. The return value is the value returned by
|
||||||
@code{call-with-current-continuation}, passing the arguments in a way
|
@var{proc}, or when @code{(@var{cont} @var{value})} is later invoked,
|
||||||
that they are returned by the call to
|
the return is the @var{value} passed.
|
||||||
@code{call-with-current-continuation}. Since it is legal to store the
|
|
||||||
captured continuation in a variable or to pass it to other procedures,
|
Normally @var{cont} should be called with one argument, but when the
|
||||||
it is possible that a procedure returns more than once, even if it is
|
location resumed is expecting multiple values (@pxref{Multiple
|
||||||
called only one time. This can be confusing at times.
|
Values}) then they should be passed as multiple arguments, for
|
||||||
|
instance @code{(@var{cont} @var{x} @var{y} @var{z})}.
|
||||||
|
|
||||||
|
@var{cont} may only be used from the dynamic root in which it was
|
||||||
|
created (@pxref{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 @var{proc} is not part of the continuation captured, it
|
||||||
|
runs only when the continuation is created. Often a program will want
|
||||||
|
to store @var{cont} somewhere for later use, this can be done in
|
||||||
|
@var{proc}.
|
||||||
|
|
||||||
|
The @code{call} in the name @code{call-with-current-continuation}
|
||||||
|
refers to the way a call to @var{proc} gives the newly created
|
||||||
|
continuation. It's not related to the way a call is used later to
|
||||||
|
invoke that continuation.
|
||||||
|
|
||||||
|
@code{call/cc} is an alias for @code{call-with-current-continuation}.
|
||||||
|
This is in common use since the latter is rather long.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@c FIXME::martin: Better example needed.
|
@deftypefn {C Function} SCM scm_make_continuation (int *first)
|
||||||
@lisp
|
Capture the current continuation as described above. The return value
|
||||||
|
is the new continuation, and @var{*first} is set to 1.
|
||||||
|
|
||||||
|
When the continuation is invoked, @code{scm_make_continuation} will
|
||||||
|
return again, this time returning the value (or set of multiple
|
||||||
|
values) passed in that invocation, and with @var{*first} set to 0.
|
||||||
|
@end deftypefn
|
||||||
|
|
||||||
|
@sp 1
|
||||||
|
@noindent
|
||||||
|
Here is a simple example,
|
||||||
|
|
||||||
|
@example
|
||||||
(define kont #f)
|
(define kont #f)
|
||||||
(call-with-current-continuation
|
(format #t "the return is ~a\n"
|
||||||
(lambda (k)
|
(call/cc (lambda (k)
|
||||||
(set! kont k)
|
(set! kont k)
|
||||||
1))
|
1)))
|
||||||
@result{}
|
@result{} the return is 1
|
||||||
1
|
|
||||||
|
|
||||||
(kont 2)
|
(kont 2)
|
||||||
@result{}
|
@result{} the return is 2
|
||||||
2
|
@end example
|
||||||
@end lisp
|
|
||||||
|
@code{call/cc} captures a continuation in which the value returned is
|
||||||
|
going to be displayed by @code{format}. The @code{lambda} stores this
|
||||||
|
in @code{kont} and gives an initial return @code{1} which is
|
||||||
|
displayed. The later invocation of @code{kont} resumes the captured
|
||||||
|
point, but this time returning @code{2}, which is displayed.
|
||||||
|
|
||||||
|
When Guile is run interactively, a call to @code{format} like this has
|
||||||
|
an implicit return back to the read-eval-print loop. @code{call/cc}
|
||||||
|
captures that like any other return, which is why interactively
|
||||||
|
@code{kont} will come back to read more input.
|
||||||
|
|
||||||
|
@sp 1
|
||||||
|
C programmers may note that @code{call/cc} is like @code{setjmp} in
|
||||||
|
the way it records at runtime a point in program execution. A call to
|
||||||
|
a continuation is like a @code{longjmp} in that it abandons the
|
||||||
|
present location and goes to the recorded one. Like @code{longjmp},
|
||||||
|
the value passed to the continuation is the value returned by
|
||||||
|
@code{call/cc} on resuming there. However @code{longjmp} can only go
|
||||||
|
up the program stack, but the continuation mechanism can go anywhere.
|
||||||
|
|
||||||
|
When a continuation is invoked, @code{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.
|
||||||
|
|
||||||
|
@code{dynamic-wind} (@pxref{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.
|
||||||
|
|
||||||
|
@sp 1
|
||||||
|
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, generally continuations should 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 (@pxref{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).
|
||||||
|
|
||||||
|
|
||||||
@node Multiple Values
|
@node Multiple Values
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue