Next: Eval, Previous: Other data types, Up: Standard procedures [Contents][Index]
This chapter describes various primitive procedures which control the flow of program execution in special ways. The ‘procedure?’ predicate is also described here.
Returns #t if obj is a procedure, otherwise returns #f.
(procedure? car) ==> #t (procedure? 'car) ==> #f (procedure? (lambda (x) (* x x))) ==> #t (procedure? '(lambda (x) (* x x))) ==> #f (call-with-current-continuation procedure?) ==> #t
Proc must be a procedure and args must be a list. Calls proc with the elements of the list ‘(append (list arg1 …,) args)’ as the actual arguments.
(apply + (list 3 4)) ==> 7 (define compose (lambda (f g) (lambda args (f (apply g args))))) ((compose sqrt *) 12 75) ==> 30
The lists must be lists, and proc must be a procedure taking as many arguments as there are lists and returning a single value. If more than one list is given, then they must all be the same length. ‘Map’ applies proc element-wise to the elements of the lists and returns a list of the results, in order. The dynamic order in which proc is applied to the elements of the lists is unspecified.
(map cadr '((a b) (d e) (g h))) ==> (b e h) (map (lambda (n) (expt n n)) '(1 2 3 4 5)) ==> (1 4 27 256 3125) (map + '(1 2 3) '(4 5 6)) ==> (5 7 9) (let ((count 0)) (map (lambda (ignored) (set! count (+ count 1)) count) '(a b))) ==> (1 2) or (2 1)
The arguments to ‘for-each’ are like the arguments to ‘map’, but ‘for-each’ calls proc for its side effects rather than for its values. Unlike ‘map’, ‘for-each’ is guaranteed to call proc on the elements of the lists in order from the first element(s) to the last, and the value returned by ‘for-each’ is unspecified.
(let ((v (make-vector 5))) (for-each (lambda (i) (vector-set! v i (* i i))) '(0 1 2 3 4)) v) ==> #(0 1 4 9 16)
Forces the value of promise (see delay
,
section see Delayed evaluation). If no value has been computed for
the promise, then a value is computed and returned. The value of the
promise is cached (or “memoized”) so that if it is forced a second
time, the previously computed value is returned.
(force (delay (+ 1 2))) ==> 3 (let ((p (delay (+ 1 2)))) (list (force p) (force p))) ==> (3 3) (define a-stream (letrec ((next (lambda (n) (cons n (delay (next (+ n 1))))))) (next 0))) (define head car) (define tail (lambda (stream) (force (cdr stream)))) (head (tail (tail a-stream))) ==> 2
‘Force’ and ‘delay’ are mainly intended for programs written in functional style. The following examples should not be considered to illustrate good programming style, but they illustrate the property that only one value is computed for a promise, no matter how many times it is forced.
(define count 0) (define p (delay (begin (set! count (+ count 1)) (if (> count x) count (force p))))) (define x 5) p ==> a promise (force p) ==> 6 p ==> a promise, still (begin (set! x 10) (force p)) ==> 6
Here is a possible implementation of ‘delay’ and ‘force’. Promises are implemented here as procedures of no arguments, and ‘force’ simply calls its argument:
(define force (lambda (object) (object)))
We define the expression
(delay <expression>)
to have the same meaning as the procedure call
(make-promise (lambda () <expression>))
as follows
(define-syntax delay (syntax-rules () ((delay expression) (make-promise (lambda () expression))))),
where ‘make-promise’ is defined as follows:
(define make-promise (lambda (proc) (let ((result-ready? #f) (result #f)) (lambda () (if result-ready? result (let ((x (proc))) (if result-ready? result (begin (set! result-ready? #t) (set! result x) result))))))))
Rationale: A promise may refer to its own value, as in the last example above. Forcing such a promise may cause the promise to be forced a second time before the value of the first force has been computed. This complicates the definition of ‘make-promise’.
Various extensions to this semantics of ‘delay’ and ‘force’ are supported in some implementations:
(eqv? (delay 1) 1) ==> unspecified (pair? (delay (cons 1 2))) ==> unspecified
(+ (delay (* 3 7)) 13) ==> 34
Proc must be a procedure of one
argument. The procedure ‘call-with-current-continuation’ packages
up the current continuation (see the rationale below) 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 use the continuation that was in effect
when the escape procedure was created. Calling the escape procedure
may cause the invocation of before and after thunks installed using
dynamic-wind
.
The escape procedure accepts the same number of arguments as the continuation to the original call to call-with-current-continuation. Except for continuations created by the ‘call-with-values’ procedure, all continuations take exactly one value. The effect of passing no value or more than one value to continuations that were not created by call-with-values is unspecified.
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.
The following examples show only the most common ways in which ‘call-with-current-continuation’ 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-with-current-continuation’.
(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
Rationale:
A common use of ‘call-with-current-continuation’ is for structured, non-local exits from loops or procedure bodies, but in fact ‘call-with-current-continuation’ is extremely useful for implementing a wide variety of advanced control structures.
Whenever a Scheme expression is evaluated there is a continuation wanting the result of the expression. The continuation represents an entire (default) future for the computation. If the expression is evaluated at top level, for example, then the continuation might take the result, print it on the screen, prompt for the next input, evaluate it, and so on forever. Most of the time the continuation includes actions specified by user code, as in a continuation that will take the result, multiply it by the value stored in a local variable, add seven, and give the answer to the top level continuation to be printed. Normally these ubiquitous continuations are hidden behind the scenes and programmers do not think much about them. On rare occasions, however, a programmer may need to deal with continuations explicitly. ‘Call-with-current-continuation’ allows Scheme programmers to do that by creating a procedure that acts just like the current continuation.
Most programming languages incorporate one or more special-purpose escape constructs with names like exit, ‘return’, or even goto. In 1965, however, Peter Landin [Landin65] invented a general purpose escape operator called the J-operator. John Reynolds [Reynolds72] described a simpler but equally powerful construct in 1972. The ‘catch’ special form described by Sussman and Steele in the 1975 report on Scheme is exactly the same as Reynolds’s construct, though its name came from a less general construct in MacLisp. Several Scheme implementors noticed that the full power of the
catch
construct could be provided by a procedure instead of by a special syntactic construct, and the name ‘call-with-current-continuation’ was coined in 1982. This name is descriptive, but opinions differ on the merits of such a long name, and some people use the namecall/cc
instead.
Delivers all of its arguments to its continuation.
Except for continuations created by the call-with-values
procedure, all continuations take exactly one value.
Values might be defined as follows:
(define (values . things) (call-with-current-continuation (lambda (cont) (apply cont things))))
Calls its producer argument with no values 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
Calls thunk without arguments, returning the result(s) of this call.
Before and after are called, also without arguments, as required
by the following rules (note that in the absence of calls to continuations
captured using call-with-current-continuation
the three arguments are
called once each, in order). Before is called whenever execution
enters the dynamic extent of the call to thunk and after is called
whenever it exits that dynamic extent. The dynamic extent of a procedure
call is the period between when the call is initiated and when it
returns. In Scheme, because of ‘call-with-current-continuation’, the
dynamic extent of a call may not be a single, connected time period.
It is defined as follows:
If a second call to ‘dynamic-wind’ occurs within the dynamic extent of the call to thunk and then a continuation is invoked in such a way that the afters from these two invocations of ‘dynamic-wind’ are both to be called, then the after associated with the second (inner) call to ‘dynamic-wind’ is called first.
If a second call to ‘dynamic-wind’ occurs within the dynamic extent of the call to thunk and then a continuation is invoked in such a way that the befores from these two invocations of ‘dynamic-wind’ are both to be called, then the before associated with the first (outer) call to ‘dynamic-wind’ is called first.
If invoking a continuation requires calling the before from one call to ‘dynamic-wind’ and the after from another, then the after is called first.
The effect of using a captured continuation to enter or exit the dynamic extent of a call to before or after is undefined.
(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)
Next: Eval, Previous: Other data types, Up: Standard procedures [Contents][Index]