guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Multiple values passed as single argument to procedure


From: Mark H Weaver
Subject: Re: Multiple values passed as single argument to procedure
Date: Mon, 12 Jun 2017 00:25:08 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux)

Hi Chris,

Chris Marusich <address@hidden> writes:

> Mark H Weaver <address@hidden> writes:
>
>> Use 'call-with-values', 'let-values', or 'receive' to call a procedure
>> that returns multiple values (or no values).
>>
>> If you do not use one of the above forms (or a macro that expands to one
>> of them) to call a procedure that returns multiple values, then Guile
>> will discard all but the first result.  Note that this is a
>> Guile-specific extension.  Other Scheme implementations may behave
>> differently (e.g. report an error) if multiple values (or no values) are
>> returned to a procedure call that was not done using one of the forms
>> listed above.
>
> I see.  So, this behavior is implementation-specific for Guile scheme.
>
> Is this behavior documented in the Guile reference manual?  I looked,
> but I couldn't find information about it.

Indeed, I was not able to find it either.

> So, it is not clear to me if one should rely on this behavior, or if
> it is likely to change in the future.

I would recommend against relying on this behavior, mainly because I
would consider it a bit sloppy.  However, I also think it's very
unlikely that we would ever remove this extension, because I don't
anticipate a compelling reason to remove it, and it would surely break
existing code.

> I was hoping to find this behavior documented in either
> "(guile) Multiple Values" or somewhere in "(guile) About Procedures".
> Perhaps there's a better location.  In any case, I think it would be
> helpful if this were documented in the manual.

Agreed.  "(guile) Multiple Values" is probably the appropriate place.

> Here's another question.  I've also noticed that when the 'list'
> procedure is composed with a procedure f that returns multiple values,
> the list that gets returned when calling the composition differs from
> the list that results when "manually" invoking the same composition.  An
> example will clarify what I mean:
>
> --8<---------------cut here---------------start------------->8---
> $ guile
> GNU Guile 2.2.2
> Copyright (C) 1995-2017 Free Software Foundation, Inc.
>
> Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
> This program is free software, and you are welcome to redistribute it
> under certain conditions; type `,show c' for details.
>
> Enter `,help' for help.
> scheme@(guile-user)> (define (f . _) (values 1 2))
> scheme@(guile-user)> (f)
> $1 = 1
> $2 = 2
> scheme@(guile-user)> (define g (compose list f))
> scheme@(guile-user)> (g)
> $3 = (1 2)
> scheme@(guile-user)> (list (f))
> $4 = (1)
> scheme@(guile-user)> 
> --8<---------------cut here---------------end--------------->8---
>
> In the above, I was surprised to find that $3 was not the same as $4.
> To put this another way, I was surprised to find that the composition
> via 'compose' (which returned $3) did not behave the same as the
> 'manual' composition (which returned $4).  What's going on here?

The problem is that you implemented your 'manual' composition in a way
that allows only one value to pass between the two procedures.  Remember
that when a procedure call is made without 'call-with-values' (or some
macro that uses it), and is not in tail position, then all but the first
return value is discarded.  That's what's happening in your call to 'f'
in (list (f)).  The call (f) is neither in tail position nor called
using 'call-with-values', so only one of its values is kept.

Try this instead:

  (let-values ((vals (f)))
    (apply list vals))

Or, more simply in the case where 'f' takes no arguments:

  (call-with-values f list)

I suppose you are thinking of 'compose' as being implemented like this:

  (define (compose f g)
    (lambda (x)
      (f (g x))))

and that's a fine definition for unary procedures that return a single
argument.

Here's one way to implement 'compose' that supports procedures of
arbitrary arity that return an arbitrary number of values:

  (define (compose f g)
    (lambda args-for-g
      (let-values ((args-for-f (apply g args-for-g)))
        (apply f args-for-f))))

Using 'call-with-values', it looks like this:

  (define (compose f g)
    (lambda args
      (call-with-values (lambda () (apply g args))
        f)))

I should note that the simple unary-only version of 'compose' that I
gave above produces more efficient procedures than the latter two,
because no heap allocation is required during execution of the resulting
procedure.  The more general versions of 'compose' produce procedures
that must allocate lists of arguments 'args', and 'args-for-f' and
'args-for-g' on the GC heap.

The core Guile version of 'compose' is defined in ice-9/boot-9.scm.

      Mark



reply via email to

[Prev in Thread] Current Thread [Next in Thread]