SRFI 8 defines a convenient syntax to bind an identifier to each of the values of a multiple-valued expression and then evaluate an expression in the scope of the bindings. As an instance of this pattern, consider the following excerpt from a ‘quicksort’ procedure:
(call-with-values (lambda () (partition (precedes pivot) others)) (lambda (fore aft) (append (qsort fore) (cons pivot (qsort aft)))))
Here ‘partition’ is a multiple-valued procedure that takes two arguments, a predicate and a list, and returns two lists, one comprising the list elements that satisfy the predicate, the other those that do not. The purpose of the expression shown is to partition the list ‘others’, sort each of the sublists, and recombine the results into a sorted list.
For our purposes, the important step is the binding of the identifiers ‘fore’ and ‘aft’ to the values returned by ‘partition’. Expressing the construction and use of these bindings with the call-by-values primitive is cumbersome: One must explicitly embed the expression that provides the values for the bindings in a parameterless procedure, and one must explicitly embed the expression to be evaluated in the scope of those bindings in another procedure, writing as its parameters the identifiers that are to be bound to the values received.
These embeddings are boilerplate, exposing the underlying binding mechanism but not revealing anything relevant to the particular program in which it occurs. So the use of a syntactic abstraction that exposes only the interesting parts – the identifiers to be bound, the multiple-valued expression that supplies the values, and the body of the receiving procedure – makes the code more concise and more readable:
(receive (fore aft) (partition (precedes pivot) others) (append (qsort fore) (cons pivot (qsort aft))))
The advantages are similar to those of a ‘let’ expression over a procedure call with a ‘lambda’ expression as its operator. In both cases, cleanly separating a “header” in which the bindings are established from a “body” in which they are used makes it easier to follow the code.
Formals and body are defined as for ‘lambda’ (see Lambda Expressions). Specifically, formals can have the following forms (the use of ‘#!optional’ and ‘#!rest’ is also allowed in formals but is omitted for brevity):
- ‘(ident1 ... identN)’
- The environment in which the ‘receive’ expression is evaluated is extended by binding ident1, ..., identN to fresh locations. The expression is evaluated, and its values are stored into those locations. (It is an error if expression does not have exactly N values.)
- The environment in which the ‘receive’ expression is evaluated is extended by binding ident to a fresh location. The expression is evaluated, its values are converted into a newly allocated list, and the list is stored in the location bound to ident.
- ‘(ident1 ... identN . identN+1)’
- The environment in which the ‘receive’ expression is evaluated is extended by binding ident1, ..., identN+1 to fresh locations. The expression is evaluated. Its first N values are stored into the locations bound to ident1 ... identN. Any remaining values are converted into a newly allocated list, which is stored into the location bound to identN+1. (It is an error if expression does not have at least N values.)
In any case, the expressions in body are evaluated sequentially in the extended environment. The results of the last expression in the body are the values of the ‘receive’ expression.