Next: baselib iteration, Previous: baselib errors, Up: baselib [Index]
This chapter describes various primitive procedures which control the flow of program execution in special ways.
rest-args must be a list. proc should accept n
arguments, where n is number of args plus the length of
rest-args. The apply
procedure calls proc with the
elements of the list:
(append (list arg1 …) rest-args)
as the actual arguments.
If a call to apply
occurs in a tail context, the call to
proc is also in a tail context.
(apply + (list 3 4)) ⇒ 7 (define compose (lambda (f g) (lambda args (f (apply g args))))) ((compose sqrt *) 12 75) ⇒ 30
proc should accept one argument. The procedure call/cc
(which is the same as the procedure
call-with-current-continuation
) packages the current continuation
as an “escape procedure” and passes it as an argument to proc.
The escape procedure is a Scheme procedure that, if it is later called, will abandon whatever continuation is in effect at that later time and will instead reinstate the continuation that was in effect when the escape procedure was created.
Calling the escape procedure may cause the invocation of before
and after procedures installed using dynamic-wind
.
The escape procedure accepts the same number of arguments as the
continuation of the original call to call/cc
.
The escape procedure that is passed to proc has unlimited extent just like any other procedure in Scheme. It may be stored in variables or data structures and may be called as many times as desired.
If a call to call/cc
occurs in a tail context, the call to
proc is also in a tail context.
The following examples show only some ways in which call/cc
is
used. If all real uses were as simple as these examples, there would be
no need for a procedure with the power of call/cc
.
(call-with-current-continuation (lambda (exit) (for-each (lambda (x) (if (negative? x) (exit x))) '(54 0 37 -3 245 19)) #t)) ⇒ -3 (define list-length (lambda (obj) (call-with-current-continuation (lambda (return) (letrec ((r (lambda (obj) (cond ((null? obj) 0) ((pair? obj) (+ (r (cdr obj)) 1)) (else (return #f)))))) (r obj)))))) (list-length '(1 2 3 4)) ⇒ 4 (list-length '(a b . c)) ⇒ #f (call-with-current-continuation procedure?) ⇒ #t
NOTE Calling an escape procedure reenters the dynamic extent of the call to
call/cc
, and thus restores its dynamic environment.
Delivers all of its arguments to its continuation. The values
procedure might be defined as follows:
(define (values . things) (call-with-current-continuation (lambda (cont) (apply cont things))))
The continuations of all non–final expressions within a sequence of
expressions, such as in lambda
, begin
, let
,
let*
, letrec
, letrec*
, let-values
,
let*-values
, case
, and cond
forms, usually take an
arbitrary number of values.
Except for these and the continuations created by
call-with-values
, let-values
, and let*-values
,
continuations implicitly accepting a single value, such as the
continuations of ?operator and ?operands of procedure calls
or the ?test expressions in conditionals, take exactly one value.
The effect of passing an inappropriate number of values to such a
continuation is undefined.
producer must be a procedure and should accept zero arguments.
consumer must be a procedure and should accept as many values as
producer returns. The call-with-values
procedure calls
producer with no arguments and a continuation that, when passed
some values, calls the consumer procedure with those values as
arguments. The continuation for the call to consumer is the
continuation of the call to call-with-values
.
(call-with-values (lambda () (values 4 5)) (lambda (a b) b)) ⇒ 5 (call-with-values * -) ⇒ -1
If a call to call-with-values
occurs in a tail context, the call
to consumer is also in a tail context.
Implementation responsibilities: After producer returns, the implementation must check that consumer accepts as many values as consumer has returned.
before, thunk, and after must be procedures, and each should accept zero arguments. These procedures may return any number of values.
The dynamic-wind
procedure calls thunk without arguments,
returning the results of this call. Moreover, dynamic-wind
calls
before without arguments whenever the dynamic extent of the call
to thunk is entered, and after without arguments whenever
the dynamic extent of the call to thunk is exited. Thus, in the
absence of calls to escape procedures created by call/cc
,
dynamic-wind
calls before, thunk, and after, in
that order.
While the calls to before and after are not considered to be
within the dynamic extent of the call to thunk, calls to the
before and after procedures of any other calls to
dynamic-wind
that occur within the dynamic extent of the call to
thunk are considered to be within the dynamic extent of the call
to thunk.
More precisely, an escape procedure transfers control out of the dynamic
extent of a set of zero or more active dynamic-wind
calls x
… and transfer control into the dynamic extent of a set of zero
or more active dynamic-wind
calls y …. It leaves
the dynamic extent of the most recent x and calls without
arguments the corresponding after procedure. If the after
procedure returns, the escape procedure proceeds to the next most recent
x, and so on. Once each x has been handled in this
manner, the escape procedure calls without arguments the before
procedure corresponding to the least recent y. If the
before procedure returns, the escape procedure reenters the
dynamic extent of the least recent y and proceeds with the next
least recent y, and so on. Once each y has been handled
in this manner, control is transferred to the continuation packaged in
the escape procedure.
Implementation responsibilities: The implementation must check the restrictions on thunk and after only if they are actually called.
(let ((path '()) (c #f)) (let ((add (lambda (s) (set! path (cons s path))))) (dynamic-wind (lambda () (add 'connect)) (lambda () (add (call-with-current-continuation (lambda (c0) (set! c c0) 'talk1)))) (lambda () (add 'disconnect))) (if (< (length path) 4) (c 'talk2) (reverse path)))) ⇒ (connect talk1 disconnect connect talk2 disconnect) (let ((n 0)) (call-with-current-continuation (lambda (k) (dynamic-wind (lambda () (set! n (+ n 1)) (k)) (lambda () (set! n (+ n 2))) (lambda () (set! n (+ n 4)))))) n) ⇒ 1 (let ((n 0)) (call-with-current-continuation (lambda (k) (dynamic-wind values (lambda () (dynamic-wind values (lambda () (set! n (+ n 1)) (k)) (lambda () (set! n (+ n 2)) (k)))) (lambda () (set! n (+ n 4)))))) n) ⇒ 7
NOTE Entering a dynamic extent restores its dynamic environment.
Next: baselib iteration, Previous: baselib errors, Up: baselib [Index]