receive
: Binding to multiple values
Department of Mathematics and Computer Science, Grinnell College, Grinnell, Iowa 50112, email.
The syntax proposed in this SRFI is used in the reference implementation of SRFI-1, ``List library.''
The only mechanism that R5RS provides for binding identifiers to
the values of a multiple-valued expression is the primitive
call-with-values
. This SRFI proposes a more concise, more
readable syntax for creating such bindings.
Although R5RS supports multiple-valued expressions, it provides
only the essential procedures values
and
call-with-values
. It is evident that the authors expected
Scheme programmers to define other constructs in terms of these,
abstracting common patterns of use.
One such pattern consists in binding an identifier to each of the values of a multiple-valued expression and then evaluating 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.
(receive
<formals> <expression>
<body>)
library syntax
<Formals>, <expression>, and <body> are as described in R5RS. Specifically, <formals> can have any of three forms:
(
<variable1> ...
<variablen>)
: The environment in which
the receive
-expression is evaluated is extended by binding
<variable1>, ...,
<variablen> 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.)
<variable>: The environment in which the
receive
-expression is evaluated is extended by binding
<variable> 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 <variable>.
(
<variable1> ...
<variablen> . <variablen +
1>)
: The environment in which
the receive
-expression is evaluated is extended by binding
<variable1>, ...,
<variablen + 1> to fresh locations. The
<expression> is evaluated. Its first n values are stored into
the locations bound to <variable1> ...
<variablen>. Any remaining values are converted
into a newly allocated list, which is stored into the location bound to
<variablen + 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.
(define-syntax receive
(syntax-rules ()
((receive formals expression body ...)
(call-with-values (lambda () expression)
(lambda formals body ...)))))
Copyright (C) John David Stone (1999). All Rights Reserved.
This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Scheme Request For Implementation process or editors, except as needed for the purpose of developing SRFIs in which case the procedures for copyrights defined in the SRFI process must be followed, or as required to translate it into languages other than English.
The limited permissions granted above are perpetual and will not be revoked by the authors or their successors or assigns.
This document and the information contained herein is provided on an "AS IS" basis and THE AUTHORS AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.