## Title

Procedures and Syntax for Multiple Values

## Author

Marc Nieper-Wißkirchen

## Status

This SRFI is currently in draft status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to `srfi-210@nospamsrfi.schemers.org`. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

- Received: 2020-08-28
- 60-day deadline: 2020-10-28
- Draft #1 published: 2020-08-29
- Draft #2 published: 2020-08-31
- Draft #3 published: 2020-09-09
- Draft #4 published: 2020-11-27

## Abstract

This SRFI extends the Scheme standard with a number of procedures and syntax dealing with multiple values, including syntax to create lists and vectors from expressions returning multiple values and procedures returning the elements of a list or vector as multiple values.

## Issues

1. Shall the library described in this SRFI re-export, for completeness, various syntax (like `let-values`, `receive`) and procedures (like `values`, `call-with-values`) dealing with multiple values from the standard libraries and other established SRFIs?
2. More (fundamental or convenience) procedures and syntax dealing with multiple values could always be added to this SRFI.

## Rationale

At its core, Scheme's evaluation semantics is multiple-value based. Continuations can accept an arbitrary number of values and expressions can yield an arbitrary number of values. This is contrast to the functional languages ML and Haskell.

Despite this fact, programming with multiple values is more cumbersome than programming with single values. This is mostly due to the fact that Scheme's application syntax does not deal directly with operands returning multiple values so that the programmer has to fall back on things like call-with-values.

To make programming with multiple values appear more natural, convenient and efficient, this SRFI defines a number of procedures and syntax dealing with multiple values.

In the R5RS, there were basically two procedures dealing with multiple values, `values` and `call-with-values`. SRFI 8 and SRFI 11 added binding constructs for multiple values. Yet, these do not cover all most-used idioms when dealing with multiple values. Thus the need for this complementary SRFI.

Some of the syntax and procedures defined here also appear in SRFI 71, albeit under different names. SRFI 71, however, is mostly concerned with extending the `let` keyword. To implement it, one needs a mechanism to change this keyword.

## Specification

When this specification talks about boxes, it refers to the disjoint Scheme type described by SRFI 111 (single valued) and SRFI 195 (multiple-valued).

### Syntax

`(apply/mv operator operand1 … producer)`

Syntax: `Operator`, each `operand`, and `producer` can be any expression.

Semantics: An `apply/mv` expression is evaluated as follows: `Operator`, each `operand`, and `producer` are evaluated in an unspecified order. The procedure resulting from evaluating `operator` is then tail-called with the single values of the `operand`s and the multiple values of `producer` as the actual arguments. The values returned by the procedure are returned.

The `apply/mv` syntax could be defined by

        (define-syntax apply/mv
          (syntax-rules ()
            ((apply/mv operator operand1 ... producer)
             (letrec-syntax
                 ((aux (syntax-rules ::: ()
                         ((aux %operator () ((%operand1 arg1) :::) %producer)
                          (let-values (((proc) %operator)
                                       ((arg1) %operand1) :::
                                       (args %producer))
                            (apply proc arg1 ::: args)))
                         ((aux %operator (%operand1 operand2 :::) (temp :::) %producer)
                          (aux %operator (operand2 :::) (temp ::: (%operand1 arg1))
                               %producer)))))
               (aux operator (operand1 ...) () producer)))))

A trick is used to generate the temporary names needed to avoid specifying the order in which the values are evaluated.

        (apply/mv string #\a (values #\b #\c))   ⟹ "abc"

`(call/mv consumer producer1 …)`

Syntax: `Consumer` and each `producer` can be any expression.

Semantics: An `call/mv` expression is evaluated as follows: `Consumer` and each `producer` are evaluated in an unspecified order. The procedure resulting from evaluating `consumer` is then tail-called with the mutiple values of the `producers`s in that order as the actual arguments. The values returned by the procedure are returned.

Note: This syntax is equivalent to the Common Lisp function `multiple-value-call`.

The `call/mv` syntax could be defined by

        (define-syntax call/mv
          (syntax-rules ()
            ((call/mv consumer producer1 ...)
             (letrec-syntax
                 ((aux (syntax-rules ::: ()
                         ((aux %consumer () ((%producer1 args1) :::))
                          (let-values (((proc) %consumer)
                                       (args1 %producer1) :::)
                            (apply proc (append args1 :::))))
                         ((aux %consumer (%producer1 producer2 :::) (temp :::))
                          (aux %consumer (producer2 :::) (temp ::: (%producer1 args1)))))))
               (aux consumer (producer1 ...) ())))))

        (call/mv string (values #\a #\b) (values #\c #\d))   ⟹ "abcd"

`(list/mv element1 … producer)`

Syntax: Each `element` and `producer` can be any expression.

Semantics: A `list/mv` expression is evaluated as follows: Each `element` and `producer` are evaluated in an unspecified order. A list constructed from the single values of the `elements`s and the multiple values of `producer` is then returned.

The `list/mv` syntax could be defined by

        (define-syntax list/mv
          (syntax-rules ()
            ((list/mv element1 ... producer)
             (apply/mv list element1 ... producer))))

        (list/mv 'a (values 'b 'c))   ⟹ (a b c)

`(vector/mv element1 … producer)`

Syntax: Each `element` and `producer` can be any expression.

Semantics: A `vector/mv` expression is evaluated as follows: Each `element` and `producer` are evaluated in an unspecified order. A vector constructed from the single values of the `elements`s and the multiple values of `producer` is then returned.

The `vector/mv` syntax could be defined by

        (define-syntax vector/mv
          (syntax-rules ()
            ((vector/mv element1 ... producer)
             (apply/mv vector element1 ... producer))))

        (vector/mv 'a (values 'b 'c))   ⟹ #(a b c)

`(box/mv element1 … producer)`

Syntax: Each `element` and `producer` can be any expression.

Semantics: A `box/mv` expression is evaluated as follows: Each `element` and `producer` are evaluated in an unspecified order. A (SRFI 195) box is constructed from the single values of the `elements`s and the multiple values of `producer` is then returned.

The `box/mv` syntax could be defined by

        (define-syntax box/mv
          (syntax-rules ()
            ((box/mv element1 ... producer)
             (apply/mv box element1 ... producer))))

        (unbox (box/mv 'a (values 'b 'c)))   ⟹ a b c

`(value/mv index operand1 … producer)`

Syntax: `Index`, each `operand`, and `producer` can be any expression.

Semantics: A `value/mv` expression is evaluated as follows: `Index`, each `element` and `producer` are evaluated in an unspecified order. It is an error if the value i of `index` is not an exact non-negative index. The ith element of the list consisting of the single values of the `operands`s and the multiple values of `producer` is then returned. It is an error if there is no such element.

The `value/mv` syntax could be defined by

        (define-syntax value/mv
          (syntax-rules ()
            ((value/mv index operand1 ... producer)
             (apply/mv value index operand1 ... producer))))

        (value/mv 1 'a (values 'b 'c))   ⟹ b

`(arity producer)`

Syntax: `Producer` can be any expression.

Semantics: An `arity` expression is evaluated as follows: `Producer` is evaluated and then the number of resulting values is returned.

The `arity` syntax could be defined by

        (define-syntax arity
          (syntax-rules ()
            ((arity producer)
             (let-values ((res producer))
               (length res)))))

        (arity (values 'a 'b 'c))   ⟹ 3

`(set!-values formals producer)`

Syntax: `formals` is a formal arguments list and `producer` can be any expression.

Semantics: A `set!-values` expression is evaluated as follows: `Producer` is evaluated and the resulting values are stored in the locations to which the variables of the `formals` are bound, where the `formals` are matched to the resulting values in the same way that the `formals` in a `lambda` expression are matched to the arguments in a procedure call. The result of the `set!-values` expression is unspecified.

The `set!-values` syntax could be defined by

        (define-syntax set!-values
          (syntax-rules ()
            ((set!-values (var1 ...) producer)
             (letrec-syntax
                 ((aux (syntax-rules ::: ()
                         ((aux () ((%var1 temp1) :::) %producer)
                          (let-values (((temp1 ::: . temp*) %producer))
                            (set! %var1 temp1) :::))
                         ((aux (%var1 var2 :::) (temp :::) %producer)
                          (aux (var2 :::) (temp ::: (%var1 temp1)) %producer)))))
               (aux (var1 ... ) () producer)))
            ((set!-values (var1 ... . var*) producer)
             (letrec-syntax
                 ((aux (syntax-rules ::: ()
                         ((aux () ((%var1 temp1) ::: (%var* temp*)) %producer)
                          (let-values (((temp1 ::: . temp*) %producer))
                            (set! %var1 temp1) :::
                            (set! %var* temp*)))
                         ((aux (%var1 var2 :::) (temp :::) %producer)
                          (aux (var2 :::) (temp ::: (%var1 temp1)) %producer)))))
               (aux (var1 ... var*) () producer)))
            ((set!-values var* producer)
             (let-values ((temp*) producer)
               (set! var* temp*)))))

        (let ((x #f) (y #f))
          (set!-values (x . y) (values 'a 'b))
          (list x y))                            ⟹ (a (b))

`(with-values producer consumer)`

Syntax: `Producer` and `consumer` can be any expressions.

Semantics: A `with-values` expression is evaluated as follows: `Producer` and `consumer` are evaluated in an unspecified order. The procedure resulting from evaluating `consumer` is then tail-called with the multiple values of `producer` as the actual arguments, and its return values are returned.

The `with-values` syntax could be defined by

        (define-syntax with-values
          (syntax-rules ()
            ((with-values producer consumer)
             (apply/mv consumer producer))))

        (with-values (values 4 5)
          (lambda (a b) b))         ⟹ 5

`(case-receive producer clause …)`

Syntax: `producer` can be any expression and each `clause` is of the form `(formals body)` as in a `case-lambda` expression.

Semantics: A `case-receive` expression is evaluated as follows: `Producer` is evaluated and then the first clause for which the resulting values agree with `formals` is selected, where agreement is specified as for the `formals` of a `lambda` expression. The variables of `formals` are bound to fresh locations, the values are stored in those locations, the `body` is evaluated in the extended environment, and the results of `body` are returned as the results of the `case-receive` expression.

The `case-receive` syntax could be defined by

        (define-syntax case-receive
          (syntax-rules ()
            ((case-receive producer clause ...)
             (with-values producer
               (case-lambda clause ...)))))

        (case-receive (values 'a 'b)
          ((x) #f)
          ((x . y) (list x y)))        ⟹ (a (b))

`(bind/mv producer transducer1 …)`

Syntax: `Producer` and the `transducers` can be any expressions.

Semantics: A `bind/mv` expression is evaluated as follows: `Producer` and the `transducers` are evaluated in an unspecified order. The procedure resulting from evaluating `transducer1`is then applied to the values resulting from evaluating `producer`. The resulting values are then applied to `transducer2`, and so on until the final `transducer` is tail-called and the resulting values are returned as the result of the `bind/mv` expression.

The `bind/mv` syntax could be defined by

        (define-syntax bind/mv
          (syntax-rules ()
            ((bind/mv producer transducer ...)
             (bind/list (list/mv producer) transducer ...))))

        (bind/mv (values 1 2 3)
                 (map-values (lambda (x) (* 2 x)))
                 (map-values (lambda (x) (+ 1 x))))   ⟹ 3 5 7

### Procedures

`(list-values list)`

It is an error if `list` is not a list.

The `list-values` procedure yields the elements of `list` as multiple values. It could be defined by

        (define (list-values lis)
          (apply values lis))

        (list-values '(a b c))   ⟹ a b c

`(vector-values vec)`

It is an error if `vec` is not a vector.

The `vector-values` procedure yields the elements of `vec` as multiple values. It could be defined by

        (define (vector-values vec)
          (list-values (vector->list vec)))

        (vector-values #(a b c))   ⟹ a b c

`(box-values box)`

It is an error if `box` is not a box.

The `box-values` procedure yields the elements of `box` as multiple values. Equivalent to `unbox`. It could be defined by

        (define box-values unbox)

        (box-values (box 'a 'b 'c))   ⟹ a b c

`(value index obj0 … objn-1)`

It is an error if `index` is not an exact non-negative integer less than `n`.

The `value` procedure simply returns its argument `objindex`. It could be defined by

        (define (value k . objs)
          (list-ref objs k))

        (value 1 'a 'b 'c)   ⟹ b

`(identity obj1 …)`

The `identity` procedure yields the objects `objs` as multiple values. Equivalent to `values`. It could be defined by

          (define identity values)

        (identity 1 2 3)   ⟹ 1 2 3

`(compose transducer1 …)`

It is an error if the `transducers` are not procedures.

The `compose` procedure yields the functional composition of the `transducers`. When the resulting procedure is applied to arguments, these arguments are passed to `transducer1`, whose results are passed to `transducer2`, and so on until the final results are returned. It could be defined by

        (define compose
          (case-lambda
            (() identity)
            ((transducer . transducers)
             (let f ((transducer transducer) (transducers transducers))
               (if (null? transducers)
                   transducer
                   (let ((composition (f (car transducers) (cdr transducers))))
                     (lambda args
                       (apply/mv composition (apply transducer args)))))))))

        (let ((f (map-values (lambda (x) (* 2 x))))
              (g (map-values (lambda (x) (+ x 1)))))
           ((compose f g) 1 2 3)))                     ⟹ 3 5 7

`(map-values proc)`

It is an error if `proc` is not a procedure.

The `map-values` procedure returns a procedure that accepts an arbitrary number of arguments, applies `proc` to each of them in an unspecified order and returns the resulting single values as multiple values. It could be defined by

        (define (map-values proc)
          (lambda args
            (list-values (map proc args))))

        ((map-values odd?) 1 2 3))   ⟹ #t #f #t

(bind/list list transducer1 …)

It is an error if `list` is not a list and the `transducers` are not procedures.

The `bind` procedure applies the elements of `list` to `transducer1`, whose results are then applied to `transducer2`, and so on until the final `transducer` is tail-called and the resulting values are returned. It could be defined by

        (define (bind/list lis . transducers)
          (list-values (fold (lambda (transducer lis)
                               (list/mv (apply transducer lis)))
                             lis transducers)))

        (bind/list '(1 2 3) (map-values (lambda (x) (* 3 x))))   ⟹ 3 6 9

(bind/box box transducer1 …)

It is an error if `box` is not a box and the `transducers` are not procedures.

The `bind` procedure applies the values of `box` to `transducer1`, whose results are then applied to `transducer2`, and so on until the final `tranducer` is tail-called and the resulting values are returned. It could be defined by

        (define (bind/box bx . transducers)
          (apply bind/list (list/mv (unbox bx)) transducers))

        (bind/box (box 1 2 3) (map-values (lambda (x) (* 3 x))))   ⟹ 3 6 9

(bind obj transducer1 …)

It is an error if the `transducers` are not procedures.

The `bind` procedure applies the `obj` to `transducer1`, whose results are then applied to `transducer2`, and so on until the final `tranducer` is tail-called and the resulting values are returned. It could be defined by

        (define (bind obj . transducers)
          (apply bind/list (list obj) transducers))

        (bind 1 (lambda (x) (values (* 3 x) (+ 1 x))))   ⟹ 3 2

## Implementation

A portable R7RS implementation can be found in the official repository of this SRFI and can also be deduced from the complete definitions of all syntax and procedures given above.

Implementers are highly encouraged to distribute specialized implementations with their Schemes that implement the described syntax and procedures in the most efficient way.

## Acknowledgements

A discussion on the SRFI 195 mailing list eventually led to this SRFI. I would like to thank all participants of this discussion for their contributions.

John Cowan suggested to add the equivalent of Common Lisp's procedure `multiple-value-call`.

Jim Rees read the example code in the first draft carefully.

Wolfgang Corcoran-Mathe made an extensive review and shared his helpful comments on the mailing list.

The idea of the `with-values` form comes from the paper An Efficient Implementation of Multiple Return Values in Scheme by J. Michael Ashley and R. Kent Dybvig.

## Copyright

© 2020 Marc Nieper-Wißkirchen.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.