From c14d283b5e830eb0f9054e51986545b9ba15f247 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Thu, 31 Aug 2017 23:46:25 -0700 Subject: [PATCH] doc: Discuss Guile-specific behavior related to multiple values. * doc/ref/api-control.texi (Multiple Values): Explain (and caution against relying upon) Guile-specific behavior when passing multiple values as a single argument to a procedure. * doc/ref/api-procedures.texi (Higher-Order Functions): Explain (and caution against relying upon) potentially counter-intuitive behavior that can arise when attempting to mimic the behavior of the 'compose' procedure by naively chaining together multi-valued procedure calls. --- doc/ref/api-control.texi | 30 +++++++++++++++++++++++++++ doc/ref/api-procedures.texi | 50 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/doc/ref/api-control.texi b/doc/ref/api-control.texi index 2d696ea89..46da2f17f 100644 --- a/doc/ref/api-control.texi +++ b/doc/ref/api-control.texi @@ -902,6 +902,36 @@ procedure body. @code{call-with-values} combines a procedure returning multiple values with a procedure which accepts these values as parameters. +You might sometimes be tempted to try to pass multiple values as a +single argument to a procedure (e.g., when directly passing the output +of one procedure call into another as input). This works fine for +single values, but caution is required when multiple values are +involved. The correct way to pass multiple values as arguments to a +procedure is to use @code{call-with-values} or something else that uses +it, such as @code{let-values} (@pxref{SRFI-11}) or @code{compose} +(@pxref{Higher-Order Functions}). If you attempt to pass multiple +values as a single argument to a procedure, Guile will only use the +first value as the argument. For example, consider the following: + address@hidden +scheme@@(guile-user)> (values 1 2) +$1 = 1 +$2 = 2 +scheme@@(guile-user)> (list (values 1 2)) +$3 = (1) address@hidden example + +Here, the @code{list} procedure is being called with a single argument. +Therefore, even though the expression @code{(values 1 2)} evaluates to +multiple values, Guile will only use the first value as the argument, so +the result is the same as if we had written @code{(list 1)}. + +This behavior is specific to the Guile implementation of Scheme. In +R6RS, the behavior when attempting to pass multiple values as a single +argument to a procedure (and in some other situations, too) is +explicitly left unspecified. Other implementations might behave +differently, so you should think twice before relying upon it. + @rnindex values @deffn {Scheme Procedure} values arg @dots{} @deffnx {C Function} scm_values (args) diff --git a/doc/ref/api-procedures.texi b/doc/ref/api-procedures.texi index df24178f9..bf97f7038 100644 --- a/doc/ref/api-procedures.texi +++ b/doc/ref/api-procedures.texi @@ -684,7 +684,10 @@ Return a procedure with the same arity as @var{proc} that returns the Compose @var{proc1} with the procedures @var{proc2} @dots{} such that the last @var{proc} argument is applied first and @var{proc1} last, and return the resulting procedure. The given procedures must have -compatible arity. +compatible arity; the first value returned by procedure @code{N} will be +passed into procedure @code{N-1} as its first argument, the second value +returned by procedure @code{N} will be passed into procedure @code{N-1} +as its second argument, etc. @lisp (procedure? (compose 1+ 1-)) @result{} #t @@ -695,6 +698,51 @@ compatible arity. ((compose zip unzip2) '((1 2) (a b))) @result{} ((1 2) (a b)) @end lisp + +When a procedure other than @var{proc1} returns multiple values, the +behavior observed when calling the procedures one after another might +differ from the behavior observed when calling their composition, +depending on how the procedures are called. This can lead to +potentially counter-intuitive results like the following: + address@hidden +scheme@@(guile-user)> (define (g) (values 1 2)) +scheme@@(guile-user)> (g) +$1 = 1 +$2 = 2 +scheme@@(guile-user)> (define composition (compose list g)) +scheme@@(guile-user)> (composition) +$3 = (1 2) +scheme@@(guile-user)> (list (g)) +$4 = (1) +scheme@@(guile-user)> address@hidden example + +At first blush, it might seem intuitive that the expressions address@hidden(composition)} and @code{(list (g))} ought to evaluate to the same +result. This intuition would be correct if the procedure @code{g} +returned only a single value. However, it returns multiple values, and +when you try to pass multiple values as a single argument to a +procedure, Guile will only use the first value as the argument +(@pxref{Multiple Values}). Therefore, the result of evaluating address@hidden(list (g))} is the same as if we had written @code{(list 1)}. It +is not a correct composition, and you should think twice before relying +on this Guile-specific behavior. + +It is possible to correctly compose procedures like these without using address@hidden, but it requires the use of @code{call-with-values} or +something else that uses it. For example, the following correctly +composes @code{list} with @code{g}: + address@hidden +scheme@@(guile-user)> (call-with-values g list) +$5 = (1 2) address@hidden example + +In fact, this is essentially what the implementation of @code{compose} +does: it recursively uses @code{call-with-values} to correctly compose +all the functions. + @end deffn @deffn {Scheme Procedure} identity x -- 2.14.1