guile-user
[Top][All Lists]
Advanced

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

Re: case-lambda* question


From: Andy Wingo
Subject: Re: case-lambda* question
Date: Mon, 14 Jan 2013 11:39:26 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.2 (gnu/linux)

Hi Daniel,

On Mon 12 Nov 2012 14:54, Daniel Llorens <address@hidden> writes:

> (define f
>       (case-lambda*
>               ((a b c #:key x) 3)
>               ((a #:key x) 1)))
>
> scheme@(guile-user)> (g 0 #:x 1)
> $1 = 3
>
> The manual says
>
>> Also, for completeness. Guile defines case-lambda* as well, which is
>> like case-lambda, except with lambda* clauses. A case-lambda* clause
>> matches if the arguments fill the required arguments, but are not too
>> many for the optional and/or rest arguments.
>> 
>> Keyword arguments are possible with case-lambda*, but they do not
>> contribute to the “matching” behavior. That is to say, case-lambda*
>> matches only on required, optional, and rest arguments, and on the
>> predicate; keyword arguments may be present but do not contribute to

I think this mention of a "predicate" probably refers to a brief time
when each clause could have a predicate to determine if it matched;
probably needs fixing.  Fixed the docs.

>> the “success” of a match. In fact a bad keyword argument list may
>> cause an error to be raised.
>
> I see that it gives 3 because ‘a b c’ matches ‘0 #:x 1’, so it's
> counting #:x as an ‘argument’ by itself. This is not what I expected
> from the description above. I think that the description should make
> clear that each item in the arg list is counted as an argument for the
> purposes of matching.
>
> Moreover I think this behavior makes case-lambda* fairly useless, or
> even treacherous. I wonder what other people think.

So, the story here is fairly simple, but the result is complicated.

The principles are:

  1. Case-lambda* clauses match in order.  Order is significant.

  2. Each clause is considered in isolation.  If the clause's formal
     arguments are compatible with the actual arguments, the clause is
     chosen.

  3. Keyword arguments are not checked for compatibility.

Commentary:

  1. Straight-forward.

  2. If you have some actual arguments ARGS... and a sequence of clauses
     CLAUSE1 CLAUSE2... of the form (FORMALS BODY...), to see if CLAUSE1
     applies, consider it in isolation:

       ((lambda* FORMALS1 BODY1...) ARGS...)

     Does it apply?  Then it will be chosen.

  3. This rule is an exception to (2).

     Rationale: At runtime we try to be fast.  Calling functions with
     keyword arguments doesn't allocate anything on the heap.  Instead,
     after required and optional arguments are bound, Guile shuffles up
     the remaining arguments above the space reserved for the final
     values of the keyword arguments, and then binds them in the order
     they are passed to the function.  This process is hairy and it's
     not easy to "roll back" to the initial state of the function call
     if a keyword does not match.  Perhaps it is doable, but I was too
     scared to fully implement it.

So, in your original example:

    (define f
      (case-lambda*
        ((a b c #:key x) 3)
        ((a #:key x) 1)))
    (f 0 #:x 1)

Here we look at the first clause:

    ((lambda* (a b c #:key x) 3) 0 #:x 1)

Does it apply?  Yes, yes it does: the 3 required arguments consume the
three arguments.  So it is chosen, and this is correct, or at least
consistent with lambda*.  (Required arguments are taken by position
only, regardless of what kind of object they are, even for keywords.
Optional arguments are the same, _except_ within a clause that also has
#:key arguments, in which case optionals are bound to positional
arguments only while the positional arguments are not keywords.)

Daniel Hartwig gives this second example, where the clauses are reversed:

    (define f
      (case-lambda*
        ((a #:key x) 1))
        ((a b c #:key x) 3))
    (g 1 2 3)

Again, considering the first clause:

    ((lambda* (a #:key x) 3) 1 2 3)

Does it apply?  It shouldn't: there are three positional arguments,
whereas only one is expected.  However, Guile currently thinks that it
does, in the case-lambda* context, because it only checks that there is
at least one positional argument.  This is probably a bug.

The attached patch fixes the problem.  If there are no comments against
it, I will apply it to stable-2.0.  It does introduce a slight
incompatibility, but I don't think there are many case-lambda* users,
and it makes cases work that previously failed to work.

Andy

Attachment: 0001-case-lambda-clauses-fail-to-match-if-too-many-positi.patch
Description: Text Data

-- 
http://wingolog.org/

reply via email to

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