[previous] [up] [next]     [contents] [index]
Next: Basic Syntax Extensions Up: PLT MzScheme: Language Manual Previous: Thanks

Multiple Return Values

Most Scheme expressions return a single value. For example, in the following procedure:

  (define quotient-and-remainder
    (lambda (n d)
      (let ([q (quotient n d)]
            [r (remainder n d)])
        (cons q r))))

the expressions (quotient n d) and (remainder n d) each return a single value. The expression (cons q r) also returns a single value; although two other values can be extracted from the cons-cell created by cons, the cons-cell is itself just a single value. Thus, this quotient-and-remainder procedure returns a single value.

On the other hand, a context using quotient-and-remainder is probably interested only in the two numbers returned by the procedure, not the cons-cell used to store the numbers. A client context might be best expressed with the let-values form:

  (let-values ([(q r) (quotient-and-remainder-values n d)])
    (printf "~a * ~a + ~a = ~a~n" q d r n))

This let-values form expresses the idea that quotient-and-remainder-values returns two values, and these two values are bound directly to q and r. For the let-values expression to be correct, the quotient-and-remainder-values procedure must specifically return two values by using the values procedure:

  (define quotient-and-remainder-values
    (lambda (n d)
      (values (quotient n d)
              (remainder n d))))

Any MzScheme expression can return multiple values. Multiple return values are generated by the values procedure, which bundles and returns its arguments as multiple return values. A bundle of return values is not itself a first-class value. Rather, the individual values must unbundled by the context of the expression returning multiple values. A run-time error is signalled when multiple values are returned to a context expecting a single value. For example, the following expression signals a run-time error:

   (let ([x (values 1 2)]) x)

In this example, the expression for x's value must return a single value (since x is a single variable), but (values 1 2) returns multiple values to the binding context. Most contexts are like this one, expecting a single value. The let-values form creates a context expecting (a particular number of) mulitple values. The call-with-values procedure also creates a multiple values return context by transforming the individual values of a multiple-value return into the arguments of a procedure call.

Binding form contexts for multiple-value expressions are discussed in section 3.5. Only the values and call-with-values procedures are described here:

Multiple return values are legal whenever the return value of an expression is ignored, or when it passed on as the result of an enclosing expression (provided that the enclosing expression can legally return multiple values). For example, either branch of an if expression can return multiple values; in this case the result of the entire if expression might be multiple values. However, the test expression of an if expression must return a single value because that value is used as a test. Similarly, the body expressions of a let form can return multiple values, but the binding expressions must return single values. (This is true even when the variable that is bound is not used in the body of the let expression.) An expression used in a procedure application (either as the procedure to be applied or one of the arguments) must always return a single value.

If a built-in procedure takes a procedure argument, and it does not inspect the result of the supplied procedure, then the supplied procedure can return multiple values. For example, the procedure supplied to for-each can return any number of values, but the procedure supplied to map must return a single value.

When the number of values returned by an expression does not match the number of values expected by the expression's context, the exn:application:arity exception is raised (at run time).

Examples:

  (- (values 1)) ; => -1 
  (- (values 1 2)) ; => exn:application:arity, returned 2 values to single-value context
  (- (values)) ; => exn:application:arity, returned 0 values to single-value context
  (call-with-values 
    (lambda () (values 1 2)) 
    (lambda (x y) y)) ; => 2 
  (call-with-values 
    (lambda () (values 1 2)) 
    (lambda z z)) ; => (1 2) 
  (call-with-values 
    (lambda () (let/cc k (k 3 4))) 
    (lambda (x y) y)) ; => 4 
  (call-with-values 
    (lambda () (values 'hello 1 2 3 4)) 
    (lambda (s . l) 
      (format "~s = ~s" s l))) ; => "hello = (1 2 3 4)"


[previous] [up] [next]     [contents] [index]
Next: Basic Syntax Extensions Up: PLT MzScheme: Language Manual Previous: Thanks

PLT