From: Vladimir V. Zolotych
Subject: &rest and &key
Date: 
Message-ID: <3BA1AFE2.4C8B76B6@eurocom.od.ua>
Hi

Could you explain me when constructs like

  &rest a &key &allow-other-keys

in lambda list might be useful ? Which way they
could be used ?

thanks in advance

-- 
Vladimir Zolotych                         ······@eurocom.od.ua

From: Kent M Pitman
Subject: Re: &rest and &key
Date: 
Message-ID: <sfwofoew5xb.fsf@world.std.com>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> Could you explain me when constructs like
> 
>   &rest a &key &allow-other-keys
> 
> in lambda list might be useful ? Which way they
> could be used ?

Sure.  The slightly more common case is to use only one or two of the keys
and then to pass the rest through.  That might look like:

 (defun open-file (filename &rest keys &key verbose &allow-other-keys)
   (when verbose (format t "~&Opening ~A~%" filename))
   (apply #'open :allow-other-keys t keys))

or sometimes

 (defun open-file (filename &rest keys &key verbose &allow-other-keys)
   (when verbose (format t "~&Opening ~A~%" filename))
   (let ((filtered-keys (copy-list keys)))
     (declare (dynamic-extent filtered-keys))
     (remf filtered-keys :verbose)
     (apply #'open keys)))

Of course, you could also do:

 (defun foo (&key x y z) (list x y z))

 (defun foo-twin (&rest keys &key &allow-other-keys)
   (apply #'foo keys))

In this case, the difference between doing this and doing

 (defun foo (&rest keys)
   (apply #'foo keys))

is that in the latter case you probably won't get static compiler arg
checking telling you that you passed an odd number of arguments to FOO.

There is a third case where this is sometimes used; when you want to specify
the key names for error checking but want to use a rest list for pass-through.
That case doesn't use &allow-other-keys, though. e.g.,

 (defun foo-twin (&rest keys &key x y z)
   (declare (ignore x y z))
   (apply #'foo keys))

This will, in most implementations, give you compile-time checking for
x y and z being the only permissible keywords.

I vaguely recall that there may be cases also where you want to just
use a rest list in methods but you can't because of congruency issues and
might have to use this idiom there literally because the other arglist 
isn't accepted.  But it's late and I'm too lazy to construct the test case.
Exercise for the reader and all that...
From: Vladimir V. Zolotych
Subject: Re: &rest and &key
Date: 
Message-ID: <3BA1E465.95EC48CF@eurocom.od.ua>
Kent M Pitman wrote:
> 
> I vaguely recall that there may be cases also where you want to just
> use a rest list in methods but you can't because of congruency issues and
> might have to use this idiom there literally because the other arglist
> isn't accepted.  But it's late and I'm too lazy to construct the test case.
> Exercise for the reader and all that...

Do you mean something like the following ?

(defmethod bar ((a (eql 1)) &rest keys &key &allow-other-keys)
  (list a keys))

(defmethod bar ((a (eql 2)) &rest keys)
  (list a keys))

(defmethod bar ((a (eql 3)) &key b c)
  (list a b c))

The SHARED-INITIALIZE could be an example of that.

-- 
Vladimir Zolotych                         ······@eurocom.od.ua
From: Frank A. Adrian
Subject: Re: &rest and &key
Date: 
Message-ID: <Hxpo7.38$_N4.233673@news.uswest.net>
Vladimir V. Zolotych wrote:

> Hi
> 
> Could you explain me when constructs like
> 
>   &rest a &key &allow-other-keys
> 
> in lambda list might be useful ? Which way they
> could be used ?

In a "real world" case, I was working on some simulation code.  Using CLOS, 
both the simulation and the simulation state had initialization parameters. 
So defining:

(defmethod initialize-instance :after ((s simulation) &rest parameters)
        (setf simulation-state (funcall #'make-instance 'state parameters)))

Allowed me to pass initialization parameters to both the simulation and its 
state in a single call:

(make-instance 'simulation :time-limit 200000
        :initial-state :random :seed 1234)

I don't know if this is good style, but it does simplify the interface to 
the simulation.

faa
From: Kaz Kylheku
Subject: Re: &rest and &key
Date: 
Message-ID: <JDqo7.10272$jY.228165@news1.rdc1.bc.home.com>
In article <·················@eurocom.od.ua>, Vladimir V. Zolotych wrote:
>Hi
>
>Could you explain me when constructs like
>
>  &rest a &key &allow-other-keys
>
>in lambda list might be useful ? Which way they
>could be used ?

&rest allows for a variable number of remaining arguments, which
are presented to the function as a single list. The need for such
flexibility arises from time to time. For example the format
function of common lisp takes a variable argument list.

Objects, in general, can have substructures that are of variable
length. For example, block has a variable number of forms in it,
or a class declaration has a variable number of slot definitions.
For that reason, Lisp supports variable length arguments within nested
argument lists, so that such objects can be destructured by macros,
even if their variable-length features are nested within the objects,
or occur in multiple places and at different nesting levels and so on.

&key specifies that the remaining arguments are key-value pairs.
The useful thing about this is that they are position independent.
The caller can omit any key that it wants, and they can appear in
any order. So again, this leads to a useful flexibility in a function
calling interface. But also, it leads to increased clarity of function
calls because the keywords, if meaningfully named, support the reader's
understanding of the arguments.  Whereas with conventional arguments,
the meaning is determined by position in the list; in a call like
(coffee 2 3) you  have no idea what 2 and 3 mean without looking at
the documentation or implementation, but in a call like 
(coffee :sugar 2 :cream 3) the meaning is more clear. Moreover, you can
specify cream without sugar or vice versa, or in either order if you
specify both.

&allow-other-keys means that the caller can specify arbitrary keys,
that are not understood by the function. They will just be ignored.
This is useful if the designer of an interface wants to anticipate
extension, or to allow for implementations to only support a subset of
the functionality specified in an interface without rejecting the
keys that are not implemented (which would violate the interface).
From: Tim Bradshaw
Subject: Re: &rest and &key
Date: 
Message-ID: <ey3r8t9zofx.fsf@cley.com>
* Kaz Kylheku wrote:
> In article <·················@eurocom.od.ua>, Vladimir V. Zolotych wrote:
>> Hi
>> 
>> Could you explain me when constructs like
>> 
>> &rest a &key &allow-other-keys
>> 
>> in lambda list might be useful ? Which way they
>> could be used ?

> &rest allows for a variable number of remaining arguments, which
> are presented to the function as a single list. The need for such
> flexibility arises from time to time. For example the format
> function of common lisp takes a variable argument list.

> [...]

However the particular case given has quite specific meaning.

A lambda list like (&rest a &key &allow-other-keys) means `there may
be some arguments, and the list of them will be bound to A.  In
addition, the arguments will all be keyword arguments.  Finally, all
keyword arguments are allowed'.  This is quite useful if you want to
do a little bit of basic syntactic checking but don't want to actually
constrain the argument list too much.  With a lambda list like the
above, for instance, you pretty much know that A could serve as a
property list.

I've used lambda lists like this to check syntactic validity of
attribute lists in a lispy *ML representation, for instance.  In my
case the thing that came in was something like

        (:a :href "foo")

and it needed to match (name &rest attributes &key &allow-other-keys)
because I didn't really care what the attributes were, just that they
were syntactically reasonable.

Incidentally it still occasionally annoys me that CL doesn't have a
feature that some other systems have had (Oaklisp is the one I know
but it may be that it came from Yale T), where you could have a
matching version of destructuring-bind - you gave it several possible
argument lists and it tried them all in turn until one matched.  I'm
not sure this is actually a feature you *want* in a language, but I
liked it.

--tim

(Oh, lambda lists like the above have slightly different meaning for
methods because of congruency rules).
From: Doug Alcorn
Subject: Re: &rest and &key
Date: 
Message-ID: <874rq14t1z.fsf@balder.seapine.com>
Tim Bradshaw <···@cley.com> writes:

> Incidentally it still occasionally annoys me that CL doesn't have a
> feature that some other systems have had (Oaklisp is the one I know
> but it may be that it came from Yale T), where you could have a
> matching version of destructuring-bind - you gave it several possible
> argument lists and it tried them all in turn until one matched.  I'm
> not sure this is actually a feature you *want* in a language, but I
> liked it.

Are you describing function overloads like C++ has?  It seems like
that feature is really only needed in a tight/static language like
C++.   Maybe I don't understand CL/CLOS well enough yet, but I would
think it's not really needed?  What am I missing?
-- 
 (__) Doug Alcorn (···········@lathi.net http://www.lathi.net)
 oo / PGP 02B3 1E26 BCF2 9AAF 93F1  61D7 450C B264 3E63 D543
 |_/  If you're a capitalist and you have the best goods and they're
      free, you don't have to proselytize, you just have to wait. 
From: Tim Bradshaw
Subject: Re: &rest and &key
Date: 
Message-ID: <ey33d5k6fwv.fsf@cley.com>
* Doug Alcorn wrote:

> Are you describing function overloads like C++ has?  It seems like
> that feature is really only needed in a tight/static language like
> C++.   Maybe I don't understand CL/CLOS well enough yet, but I would
> think it's not really needed?  What am I missing?

No, I'm describing list-destructuring which is something completely
different.  In CL there is an operator, DESTRUCTURING-BIND which
allows you to match a pattern against a list and bind some appropriate
variables:

If x is (1 2 :foo 5)

then 
(destructuring-bind (a b &key (foo 4)) x
  (values a b foo))

Will return 1 2 and 5.  But

(destructuring-bind (a b) x
  (values a b))

is an error (signals an error?).

What Oaklisp (?) had was a way of trying several patterns until one
matched.  I forget the syntax, but mabe it was something like this:

(destructuring-case x
  ((a b) (values a b))
  ((a b &key foo) (values a b c))
  ((&rest junk) junk))

--tim
From: Kent M Pitman
Subject: Re: &rest and &key
Date: 
Message-ID: <sfw66agjox8.fsf@world.std.com>
Tim Bradshaw <···@cley.com> writes:

> (destructuring-case x
>   ((a b) (values a b))
>   ((a b &key foo) (values a b c))
>   ((&rest junk) junk))

I really don't like this because of:

(destructuring-case '(:foo 3)
  ((a b) (values a b))
  ((&key foo bar) (values foo bar))
  ((&rest junk) junk))

This kind of stuff is fine for a user application, but there is a 
higher standard of care for language design.  In user applications,
you pretty much know how things will be used and you can also constrain
people not to do certain kinds of things.  In language design, you can't
know how people will use and abuse the primitives you make, and you
have to think harder to avoid problems.

Language design has to be done not just with an eye to "is this
well-defined" but also "will this lead people to do stuff that 
confuses them".  You can say the answer is "well-defind" in the
case I cite here, but I claim that it will still lead to program bugs.

Although this particular issue didn't come up in the design of the
language, I can say that things of this kind are omitted in the
language because they lead to confusions.
From: Tim Bradshaw
Subject: Re: &rest and &key
Date: 
Message-ID: <ey3k7yv32y6.fsf@cley.com>
* Kent M Pitman wrote:

> I really don't like this because of:

> (destructuring-case '(:foo 3)
>   ((a b) (values a b))
>   ((&key foo bar) (values foo bar))
>   ((&rest junk) junk))

This would match on the first thing, I think.

> This kind of stuff is fine for a user application, but there is a 
> higher standard of care for language design. 


yes, I agree.  I definitely was not suggesting this particular syntax
or (unclear) semantics.  It would obviously need a lot more thought to
put into the language, if it was worth doing at all (which I'm not at
all sure it is: just because I want it doesn't mean everyone does, and
if I cared enough to *really* want it I'd have implemented it).

--tim
From: Rahul Jain
Subject: Re: &rest and &key
Date: 
Message-ID: <87k7yxfp35.fsf@photino.sid.rice.edu>
Tim Bradshaw <···@cley.com> writes:

> Incidentally it still occasionally annoys me that CL doesn't have a
> feature that some other systems have had (Oaklisp is the one I know
> but it may be that it came from Yale T), where you could have a
> matching version of destructuring-bind - you gave it several possible
> argument lists and it tried them all in turn until one matched.  I'm
> not sure this is actually a feature you *want* in a language, but I
> liked it.

You could probably use META on lists instead of strings for this kind
of thing. The matching operation used in the prolog compiler in PAIP
seems to be overkill, but it would be good for more complex situations
(like a prolog compiler! :)

But I suppose the same system as you describe wouldn't be too hard to
implement.

-- 
-> -/-                       - Rahul Jain -                       -\- <-
-> -\- http://linux.rice.edu/~rahul -=- ·················@usa.net -/- <-
-> -/- "I never could get the hang of Thursdays." - HHGTTG by DNA -\- <-
|--|--------|--------------|----|-------------|------|---------|-----|-|
   Version 11.423.999.220020101.23.50110101.042
   (c)1996-2000, All rights reserved. Disclaimer available upon request.