unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
From: Mark H Weaver <mhw@netris.org>
To: Chris Marusich <cmmarusich@gmail.com>
Cc: guile-user@gnu.org
Subject: Re: Multiple values passed as single argument to procedure
Date: Mon, 12 Jun 2017 00:25:08 -0400	[thread overview]
Message-ID: <87a85d3k9n.fsf@netris.org> (raw)
In-Reply-To: <87zidexdjw.fsf@gmail.com> (Chris Marusich's message of "Sun, 11 Jun 2017 17:19:47 -0700")

Hi Chris,

Chris Marusich <cmmarusich@gmail.com> writes:

> Mark H Weaver <mhw@netris.org> 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	other threads:[~2017-06-12  4:25 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-06-11  7:56 Multiple values passed as single argument to procedure Chris Marusich
2017-06-11  8:28 ` David Kastrup
2017-06-11 20:36 ` Mark H Weaver
2017-06-11 21:31   ` Mark H Weaver
2017-06-12  0:19   ` Chris Marusich
2017-06-12  4:25     ` Mark H Weaver [this message]
2017-06-12  8:19       ` Chris Marusich
2017-06-12  8:55         ` Neil Jerram
2017-06-12  9:48           ` Neil Jerram
2017-06-12  9:39         ` David Kastrup
2017-06-12 11:31           ` Mark H Weaver
2017-06-12 14:24             ` David Kastrup
2017-06-13  2:26               ` Mark H Weaver
2017-06-13  3:09                 ` Mark H Weaver
2017-06-13  3:45                 ` Mark H Weaver
2017-06-13 11:17                 ` dsmich
2017-06-26 11:25                   ` Alex Vong
  -- strict thread matches above, loose matches on Subject: below --
2017-09-01 19:39 Chris Marusich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87a85d3k9n.fsf@netris.org \
    --to=mhw@netris.org \
    --cc=cmmarusich@gmail.com \
    --cc=guile-user@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).