On Scheme

Thoughts on Scheme and teaching Scheme

Archive for March 13th, 2006

The problem with values

Posted by Peter on March 13, 2006

The idea behind the values construct is that it allows a function to return more than one result at a time without having to pack them into a data structure of some kind. For example if you aren’t using some kind of error blocks then you may want functions to return both the result of their execution as well as any error codes that occurred during the execution. Another case in which you may want multiple values is when there is simply more than one piece of information to return, for example the address and phone number of a person. Admittedly anything you do with multiple return values you could do by packing them into a list and then unpacking them again, but the idea of multiple return values has its own kind of elegance.

Scheme and Common Lisp do support multiple values with the values construct, which can take any number of parameters. The problem comes when you want to use these values. For example this: (+ 1 (values 3 4)) won’t work. Even though + can take any number of arguments it expects only one value in the second position, and thus generates an error. There are two ways to get around this. One is the call-with-values construct. It takes two functions as argument, and calls the second with however many values are returned by the first. For example our example above becomes (call-with-values (lambda () (values 3 4)) (lambda (a b) (+ 1 a b))). Obviously this is quite ugly and cumbersome. Things become a little easier if we just wanted to sum the values, we could do that with (call-with-values (lambda () (values 3 4)) +) since + takes any number of arguments. Another construct is let-values. Using this construct our example would become (let-values (((a b) (values 1 2))) (+ 1 a b)).

The following common problem can be solved: we often need only the first or second value of a function that returns multiple values. The following macro takes an expression returning multiple values and a number and returns only one of the values. This solution is relatively inelegant though, because it requires turning the values into a list first.

(define-syntax val-ref
       ((val-ref expr num)
	 (list-ref (call-with-values (lambda () expr) list) num))))

Working with multiple values doesn’t always cause problems. However there is one kind of situation that can’t be fixed even with macro: the ability for multiple values to expand into the parameters of a function call directly, without call-with-values. For example if you wanted return three values from a function, one new value and two from a function that already returns multiple values you cannot simply do the following: (values myval (func a b)), instead you would have to write (call-with-values (lambda () (func a b)) (lambda (x y) (values myval x y))). And if that function had returned a variable number of values there would be no way to express the solution as far as I know. If anyone has macros / solutions to this kind of problem I would be happy to hear them.

Posted in Common Lisp, Exploring Scheme | 1 Comment »