[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12. Procedures

Procedures are created by evaluating lambda expressions (see section 2.1 Lambda Expressions); the lambda may either be explicit or may be implicit as in a "procedure define" (see section 2.4 Definitions). Also there are special built-in procedures, called primitive procedures, such as car; these procedures are not written in Scheme but in the language used to implement the Scheme system. MIT Scheme also provides application hooks, which support the construction of data structures that act like procedures.

In MIT Scheme, the written representation of a procedure tells you the type of the procedure (compiled, interpreted, or primitive):

 
pp
     =>  #[compiled-procedure 56 ("pp" #x2) #x10 #x307578]
(lambda (x) x)
     =>  #[compound-procedure 57]
(define (foo x) x)
foo
     =>  #[compound-procedure 58 foo]
car
     =>  #[primitive-procedure car]
(call-with-current-continuation (lambda (x) x))
     =>  #[continuation 59]

Note that interpreted procedures are called "compound" procedures (strictly speaking, compiled procedures are also compound procedures). The written representation makes this distinction for historical reasons, and may eventually change.

12.1 Procedure Operations  
12.2 Primitive Procedures  
12.3 Continuations  
12.4 Application Hooks  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.1 Procedure Operations

procedure: apply procedure object object ...
Calls procedure with the elements of the following list as arguments:

 
(cons* object object ...)

The initial objects may be any objects, but the last object (there must be at least one object) must be a list.

 
(apply + (list 3 4 5 6))                =>  18
(apply + 3 4 '(5 6))                    =>  18

(define compose
  (lambda (f g)
    (lambda args
      (f (apply g args)))))
((compose sqrt *) 12 75)                =>  30

procedure+: procedure? object
Returns #t if object is a procedure; otherwise returns #f. If #t is returned, exactly one of the following predicates is satisfied by object: compiled-procedure?, compound-procedure?, or primitive-procedure?.

procedure+: compiled-procedure? object
Returns #t if object is a compiled procedure; otherwise returns #f.

procedure+: compound-procedure? object
Returns #t if object is a compound (i.e. interpreted) procedure; otherwise returns #f.

procedure+: primitive-procedure? object
Returns #t if object is a primitive procedure; otherwise returns #f.

The following two procedures test the arity of a procedure, that is, the number of arguments that the procedure accepts. The results of the test may be less restrictive than the effect of calling the procedure. In other words, these procedures may indicate that the procedure will accept a given number of arguments, but if you call the procedure it may signal a condition-type:wrong-number-of-arguments error. This is because these procedures examine the apparent arity of a procedure. For example, here is a procedure that appears to accept any number of arguments, but when called will signal an error if the number of arguments is not one:

 
(lambda arguments (apply car arguments))

procedure+: procedure-arity-valid? procedure k
Returns #t if procedure accepts k arguments; otherwise returns #f.

procedure+: procedure-arity procedure
Returns a description of the number of arguments that procedure accepts. The result is a newly allocated pair whose car field is the minimum number of arguments, and whose cdr field is the maximum number of arguments. The minimum is an exact non-negative integer. The maximum is either an exact non-negative integer, or #f meaning that the procedure has no maximum number of arguments.

 
(procedure-arity (lambda () 3))         =>  (0 . 0)
(procedure-arity (lambda (x) x))        =>  (1 . 1)
(procedure-arity car)                   =>  (1 . 1)
(procedure-arity (lambda x x))          =>  (0 . #f)
(procedure-arity (lambda (x . y) x))    =>  (1 . #f)
(procedure-arity (lambda (x #!optional y) x))
                                        =>  (1 . 2)

procedure+: procedure-environment procedure
Returns the closing environment of procedure. Signals an error if procedure is a primitive procedure, or if procedure is a compiled procedure for which the debugging information is unavailable.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.2 Primitive Procedures

procedure+: make-primitive-procedure name [arity]
Name must be a symbol. Arity must be an exact non-negative integer, -1, #f, or #t; if not supplied it defaults to #f. Returns the primitive procedure called name. May perform further actions depending on arity:

#f
If the primitive procedure is not implemented, signals an error.

#t
If the primitive procedure is not implemented, returns #f.

integer
If the primitive procedure is implemented, signals an error if its arity is not equal to arity. If the primitive procedure is not implemented, returns an unimplemented primitive procedure object that accepts arity arguments. An arity of -1 means it accepts any number of arguments.

procedure+: primitive-procedure-name primitive-procedure
Returns the name of primitive-procedure, a symbol.

 
(primitive-procedure-name car)          =>  car

procedure+: implemented-primitive-procedure? primitive-procedure
Returns #t if primitive-procedure is implemented; otherwise returns #f. Useful because the code that implements a particular primitive procedure is not necessarily linked into the executable Scheme program.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.3 Continuations

procedure: call-with-current-continuation procedure
Procedure must be a procedure of one argument. Packages up the current continuation (see below) as an escape procedure and passes it as an argument to procedure. The escape procedure is a Scheme procedure of one argument that, if it is later passed a value, will ignore whatever continuation is in effect at that later time and will give the value instead to the continuation that was in effect when the escape procedure was created. The escape procedure created by call-with-current-continuation 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 uses of this procedure. If all real programs 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

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 quite useful for implementing a wide variety of advanced control structures.

Whenever a Scheme expression is evaluated a continuation exists that wants 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, the continuation will 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 don't think much about them. On the rare occasions that you may need to deal explicitly with continuations, call-with-current-continuation lets you do so by creating a procedure that acts just like the current continuation.

procedure+: continuation? object
Returns #t if object is a continuation; otherwise returns #f.

procedure+: within-continuation continuation thunk
Thunk must be a procedure of no arguments. Conceptually,
within-continuation invokes continuation on the result of invoking thunk, but thunk is executed in the dynamic context of continuation. In other words, the "current" continuation is abandoned before thunk is invoked.

procedure+: dynamic-wind before thunk after
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)

The following two procedures support multiple values.

procedure+: call-with-values thunk procedure
Thunk must be a procedure of no arguments, and procedure must be a procedure. Thunk is invoked with a continuation that expects to receive multiple values; specifically, the continuation expects to receive the same number of values that procedure accepts as arguments. Thunk must return multiple values using the values procedure. Then procedure is called with the multiple values as its arguments. The result yielded by procedure is returned as the result of call-with-values.

procedure+: values object ...
Returns multiple values. The continuation in effect when this procedure is called must be a multiple-value continuation that was created by call-with-values. Furthermore it must accept as many values as there are objects.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.4 Application Hooks

Application hooks are objects that can be applied like procedures. Each application hook has two parts: a procedure that specifies what to do when the application hook is applied, and an arbitrary object, called extra. Often the procedure uses the extra object to determine what to do.

There are two kinds of application hooks, which differ in what arguments are passed to the procedure. When an apply hook is applied, the procedure is passed exactly the same arguments that were passed to the apply hook. When an entity is applied, the entity itself is passed as the first argument, followed by the other arguments that were passed to the entity.

Both apply hooks and entities satisfy the predicate procedure?. Each satisfies either compiled-procedure?, compound-procedure?, or primitive-procedure?, depending on its procedure component. An apply hook is considered to accept the same number of arguments as its procedure, while an entity is considered to accept one less argument than its procedure.

procedure+: make-apply-hook procedure object
Returns a newly allocated apply hook with a procedure component of procedure and an extra component of object.

procedure+: apply-hook? object
Returns #t if object is an apply hook; otherwise returns #f.

procedure+: apply-hook-procedure apply-hook
Returns the procedure component of apply-hook.

procedure+: set-apply-hook-procedure! apply-hook procedure
Changes the procedure component of apply-hook to be procedure. Returns an unspecified value.

procedure+: apply-hook-extra apply-hook
Returns the extra component of apply-hook.

procedure+: set-apply-hook-extra! apply-hook object
Changes the extra component of apply-hook to be object. Returns an unspecified value.

procedure+: make-entity procedure object
Returns a newly allocated entity with a procedure component of procedure and an extra component of object.

procedure+: entity? object
Returns #t if object is an entity; otherwise returns #f.

procedure+: entity-procedure entity
Returns the procedure component of entity.

procedure+: set-entity-procedure! entity procedure
Changes the procedure component of entity to be procedure. Returns an unspecified value.

procedure+: entity-extra entity
Returns the extra component of entity.

procedure+: set-entity-extra! entity object
Changes the extra component of entity to be object. Returns an unspecified value.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Chris Hanson on July, 18 2001 using texi2html