Flux
Flux

Reputation: 10950

What is supposed to happen when a procedure's argument is given multiple values when it expects one value only?

(+ (values 1) (values 2)) returns 3. What is (+ 1 (values 2 3)) supposed to return? In R7RS-small, is the second value in a (values ...) automatically ignored when only one value is needed? In Guile 3.0.7, (+ 1 (values 2 3)) returns 3, but it gives an error in MIT Scheme 11.2 and Chibi 0.10.0.

Upvotes: 0

Views: 98

Answers (1)

ad absurdum
ad absurdum

Reputation: 21362

(+ (values 1) (values 2)) is fine, and the result should be 3. But it is not the case that unneeded values are automatically ignored in a values expression; in fact the behavior of (+ 1 (values 2 3)) is unspecified in both R6RS and R7RS Scheme.

From the entry for values from R6RS 11.15 [emphasis mine]:

Delivers all of its arguments to its continuation....

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 <operand>s 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.

R7RS has similar language in 6.10:

The effect of passing no values or more than one value to continuations that were not created in one of these ways is unspecified.

The reason that (+ (values 1) (values 2)) is ok is that continuations of operands in procedure calls take exactly one value. (values 1) and (values 2) each provide exactly one value for their respective continuations.

call-with-values is meant for connecting producers of multiple values with procedures which consume those values. The first argument to call-with-values should be a procedure which takes no arguments and produces values to be consumed. The second argument should be a procedure which accepts the number of values produced by the first procedure:

> (call-with-values (lambda () (values 2 3))
    (lambda (x y) (+ 1 x y)))
6

Note that the above use of call-with-values requires the consumer to accept the number of values produced by the producer since Scheme requires that implementations raise an error when a procedure does not accept the number of arguments passed to it:

> (call-with-values (lambda () (values 2 3 4))
    (lambda (x y) (+ 1 x y)))

Exception: incorrect argument count in call (call-with-values (lambda () (values 2 3 4)) (lambda (x y) (+ 1 x y)))

If it is desirable for extra arguments to be ignored, the consumer must be designed towards that goal. Here the consumer is designed to ignore all but its first argument:

> (call-with-values (lambda () (values 2 3 4))
    (lambda (x . xs) (+ 1 x)))
3

Upvotes: 2

Related Questions