&rest and &key 
Author Message
 &rest and &key

What is the right way to define a function that can take multiple
arguments and specified keywords?  E.g., I'd like to do something like:

(defun foo (x &rest y &key (combiner #'+))
  (apply combiner x y))

and call it with:  (foo 2 3 :combiner #'*)

But the lisp evaluator will complain about this.



Sun, 31 Jul 1994 06:43:25 GMT  
 &rest and &key

Quote:

>   What is the right way to define a function that can take multiple
>   arguments and specified keywords?  E.g., I'd like to do something like:

>   (defun foo (x &rest y &key (combiner #'+))
>   (apply combiner x y))

>   and call it with:  (foo 2 3 :combiner #'*)

>   But the lisp evaluator will complain about this.

Using both rest and key args in the same arglist is really painful. I
don't remember all the details, but I tried it once. One of the problem
is that you can't have an odd number of arguments. It's finally much
easier to specify only the rest argument and to process explicitely
the keys in the function. It gives somethig like that:

(defun get-combiner (arglist) ....

(defun conbiner-supplied-p (arglist) ....

(defun foo (x &rest arglist)
  (let* ((combiner (get-combiner arglist))
         (args (if (combiner-supplied-p arglist)
                   (remove :combiner (remove combiner arglist))
                   arglist)))
  ...

Of course, the two auxilliary functions can be placed into foo with a
flet or a labels.

--
Emmanuel Baechler.
Laboratoire d'Intelligence Artificielle
Ecole Polytechnique Federale de Lausanne
MA-Ecublens
CH-1015 Lausanne        Switzerland
Tel.: ++41-21-693-2732

Standard Disclaimer

Ban the bomb. Save the world for conventional warfare.



Sun, 31 Jul 1994 17:46:39 GMT  
 &rest and &key

Quote:

>>   What is the right way to define a function that can take multiple
>>   arguments and specified keywords?  E.g., I'd like to do something like:

>>   (defun foo (x &rest y &key (combiner #'+))
>>   (apply combiner x y))

>>   and call it with:  (foo 2 3 :combiner #'*)

My first recommendation is to find some other calling sequence.

What's wrong with this:

(defun foo (args &key (combiner #'+))
  (apply combiner args))

I'm not even sure what you expect this to do.  From looking at the lambda
list, I would expect Y to be bound to (3 :COMBINER #<FUNCTION *>), but from
the function body it appears that you expect the rest-list to have the
keyword/value pairs removed (since + and * don't allow keywords in their
arguments), i.e. Y would be bound to (3).

The normal reason to use both &rest and &key is when you also have
&allow-other-keys, e.g.

(defun bar (x &rest options &key option1 option2 ... &allow-other-keys)
  ...
  (apply #'some-other-function x :allow-other-keys t options)
  ...)

This permits BAR to take a combination of options that it uses directly and
other options that are used by SOME-OTHER-FUNCTION.  The &allow-other-keys
tells BAR not to complain about options that it doesn't recognize, and
:ALLOW-OTHER-KEYS T tells SOME-OTHER-FUNCTION to ignore the options that it
doesn't recognize.

Quote:
>Using both rest and key args in the same arglist is really painful. I
>don't remember all the details, but I tried it once. One of the problem
>is that you can't have an odd number of arguments. It's finally much
>easier to specify only the rest argument and to process explicitely
>the keys in the function. It gives somethig like that:

>(defun get-combiner (arglist) ....

>(defun conbiner-supplied-p (arglist) ....

>(defun foo (x &rest arglist)
>  (let* ((combiner (get-combiner arglist))
>         (args (if (combiner-supplied-p arglist)
>                   (remove :combiner (remove combiner arglist))
>                   arglist)))

Those uses of REMOVE aren't very safe, as there may be multiple instances
of the combiner object in ARGLIST, e.g. if arglist is

        (2 3 :COMBINER #'- :OTHER-FUNCTION #')

the result will be that ARGS is set to

        (2 3 :OTHER-FUNCTION)

which is not what you intended.

Safer would be something like:

(defun partition-args (keywords arglist)
  "Partition the argument list around the first argument that is in KEYWORDS."
  (let ((first-key (position-if #'(lambda (elt) (member elt keywords))
                                arglist)))
    (values (subseq arglist 0 first-key)
            (subseq arglist first-key))))

(defun foo (x &rest arglist)
  (multiple-value-bind (args options)
      (partition-args '(:combiner) arglist)
    (apply #'foo-internal x args options)))

(defun foo-internal (x args &key (combiner #'+))
  (apply combiner x args))

If you try to parse the keyword arguments yourself, you'll almost certainly
make mistakes.  Besides the mistake I pointed out above, you could also
have problems when the value of one option happens to be a keyword that's a
valid option name, e.g. if FOO also had a :NAME parameter it might be
called with:

        (foo 1 2 3 :name :combiner :combiner #'*)

And it's a pain to implement the rule that a keyword may appear multiple
times in an argument list but only the rightmost one has an effect and the
rest should be ignored (Emmanuel didn't show the implementation of
GET-COMBINER, so it might implement the "rightmost" rule, but he clearly
doesn't remove all of the :combiner/value pairs from the argument list (he
removes all the :combiner keywords, but not all the values).
--
Barry Margolin, Thinking Machines Corp.


{uunet,harvard}!think!barmar



Mon, 01 Aug 1994 06:13:19 GMT  
 &rest and &key
|> What is the right way to define a function that can take multiple
|> arguments and specified keywords?  E.g., I'd like to do something like:
|>
|> (defun foo (x &rest y &key (combiner #'+))
|>   (apply combiner x y))
|>
|> and call it with:  (foo 2 3 :combiner #'*)
|>
|> But the lisp evaluator will complain about this

I'm assuming the result you want from the example is (* 2 3) = 6.
The problem is that &rest when used in conjunction with &key is simply shorthand for binding a lexical variable to a list of all the &key keyword-value pairs. Therefore, the &rest argument must be even in length and may not include arbitrary data. If you must define a function then you are restricted to something like
        (defun foo (args &key (combiner #'+))
          (declare (list args)
                   (function combiner))
          (apply combiner args))

However, if you can get away with a macro, then you can play with the 'shape' of the arglist:
        (defmacro foo ((x &rest y) &key (combiner #'+))

such that (foo (2 3) :combiner #'*) expands into (apply #'* 2 (list 3)). Furthermore (foo (a b c d)) expands into (apply #'+ a (list b c d)) so the variables a, b, c and d *are* evaluated.
--
*

JPL Artificial Intelligence Group
*



Mon, 01 Aug 1994 06:50:12 GMT  
 &rest and &key

[about &rest, &key and &allow-other-keys]

I've always wondered what could justify the presence of such a mess in
the language: runtime keyword are a very fine way to mix syntax with
execution, which is pretty weird, and increases the language's
inter-tanglement of execution, load, and compile-time.

Quote:
> This permits BAR to take a combination of options that it uses directly and
> other options that are used by SOME-OTHER-FUNCTION.  The &allow-other-keys
> tells BAR not to complain about options that it doesn't recognize, and
> :ALLOW-OTHER-KEYS T tells SOME-OTHER-FUNCTION to ignore the options that it
> doesn't recognize.

As I understand it, the usage of this idiom is typically to "compose"
top-level commands, each of which offer the comfort of (primarily
syntactic) keyword binding. Of course in such a situation, the cost of
this weirdness is very low, because it is typically paid a few times
only in the outer, top-level, user-interface layers of the program.

Now, considering that a typical application does not always have a
Lisp Listener for the user to type in commands (there are better user
interfaces!), what justifies having runtime keywords *in the
language*, and not rejected as an environment-bound issue ? Notice
that anyway, (except on a Lisp machine) it doesn't help a lot for
launching an application as a shell command and passing it options,
arguments, etc. Other machinery has to be used for this.

Could anybody give figures about the actual usage of runtime (that is
computed at runtime) keywords ??? Of course, I suspect that a fair
amount of user-level commands might be built using the idiom in a
Lisp-machine environment, but I'd like to insist that these commands
are not part of the user code (so the user doesn't want to pay if they
are written the wrong way, and if the impact on the language is
negative). So my question could be reformulated in one of these ways:

        . How often are runtime keywords really used, compared for
        example to the total number of lines involved?

        . How many lines of code does it typically save when one uses
        the aforementioned idiom (this is extremely low of course, and
        as I pointed out no argument of efficiency applies).

Unless somebody proves the contrary, I'd say that the degree of
utility of runtime keywords is very low. The negative impact
of the idiom on the whole language is far too high, IMO runtime
keywords should be considered an obsolete (or at least deprecated)
feature of Common Lisp.

        V. Delacour



Mon, 01 Aug 1994 07:18:28 GMT  
 &rest and &key

Thanks for the half a dozen replies and suggestions that people
sent me.  The problem that leads to the original question is to
write a generalized cartesian product which can take any arbitrary
number of sets and a user-specifable combining function for combing
elements between sets.

The reason I don't want to bundle all arguments as a list is
purely stylistic.  It is the same reason that I don't want to
write:
        (+ '( 1 2 3 ))

I don't want to treat combiner as a required parameter because
it doesn't allow default.

I was hoping that there is some simple combination of lambda keywords
that does what I want.  For instance, a &rest* parameter that accumulates
all the remaining arguments not matched by either required or &key arguments.

The solution I like most comes from Dan Rabin and Mark Kantrowitz.
I am fleshing out their ideas with the following code:

;;;generalized product of n sets

(defun create-combiner (&optional (element-combiner #'list))
  #'(lambda (&rest l)
      (labels ((combine-2 (A B)
                 (mapcan #'(lambda(x)
                             (mapcar #'(lambda(y)
                                         (funcall element-combiner x y))
                                     B))
                         A)))
        (reduce #'combine-2 l))))

Then, I can do the standard cartesian product by:

        (funcall (create-combiner) A B C)

for some sets A, B, and C.  Or,

        (funcall (create-combiner #'append) A B C)      

to do a pairwise union of elements, which would be useful for
sets whose elements are sets.



Mon, 01 Aug 1994 08:13:20 GMT  
 &rest and &key

Quote:

>[about &rest, &key and &allow-other-keys]

>I've always wondered what could justify the presence of such a mess in
>the language: runtime keyword are a very fine way to mix syntax with
>execution, which is pretty weird, and increases the language's
>inter-tanglement of execution, load, and compile-time.
>Now, considering that a typical application does not always have a
>Lisp Listener for the user to type in commands (there are better user
>interfaces!), what justifies having runtime keywords *in the
>language*, and not rejected as an environment-bound issue ?

Keywords need a run-time existence so that you can use them with
APPLY, in something like the following:

  (defun f (g &rest args)
    ... (apply g args) ...)

If I want to be able to say

  (f #'g ...)

and pass keyword parameters to G, keywords have to be there at
run-time.

Almost anything can be seen as an environment issue.  Why try to
give a langauge a reasonable syntax at all?  Why not let the
environment provide the readable syntax?

But as far as I'm concerned, the syntax I use to write code and that's
used for the code I read is part of the language.  It has a run-time
existence because Lisp code can be read in as data.

If the env provided keywords, then either every env would provide
them, and they'd effectively be part of the langauage's syntax,
or only some environment's would provide them and we'd have trouble
reading the code in other envs.

But the balance might shift against run-time kwds if not for the
APPLY issue, even though I'm not sure I'd agree with the shift.

-- jeff



Tue, 02 Aug 1994 04:04:02 GMT  
 &rest and &key

Quote:

>   Keywords need a run-time existence so that you can use them with
>   APPLY, in something like the following:

>     (defun f (g &rest args)
>       ... (apply g args) ...)

>   If I want to be able to say

>     (f #'g ...)

>   and pass keyword parameters to G, keywords have to be there at
>   run-time.

That's right. However, most uses of apply are programming errors in
the sense that they are not robust against explicitly stated
implementation-dependent limits. Maybe apply too should be considered
'guts' of the programming system (might be useful in certain parts of
the development environment)...

Quote:

>   Almost anything can be seen as an environment issue.  Why try to
>   give a langauge a reasonable syntax at all?  Why not let the
>   environment provide the readable syntax?

>   But as far as I'm concerned, the syntax I use to write code and that's
>   used for the code I read is part of the language.  It has a run-time
>   existence because Lisp code can be read in as data.

I think this way of putting it is a bit misleading: when programs are
read in as data, they are usually made available to macro-expansion
functions, then compiled, then loaded, then executed (and even the
eval function can proceed that way). So I would not say that programs
as lists have a runtime existence, but rather that programs can be
given as lists to the compiler.

[...]

Quote:
>   But the balance might shift against run-time kwds if not for the
>   APPLY issue, even though I'm not sure I'd agree with the shift.

Yes, I think that the question is legitimate and should be debated
when the standard draft is out (I'd rather be for shifting runtime
keywords out, but other might convincely legitimate their existence).

Quote:
>   -- jeff

As a matter of facts, many points appear not to have been cleaned by
the standard committee: Common Lisp still has a strong taste of
ancient maclisp, where there was no clear distinction between
environment and language, and where some 'clever' rplac's could do
great things. I think we should not let the language be standardized
without some further discussion on certain points.

        Vincent



Tue, 02 Aug 1994 05:36:56 GMT  
 &rest and &key

Quote:
>Yes, I think that the question is legitimate and should be debated
>when the standard draft is out (I'd rather be for shifting runtime
>keywords out, but other might convincely legitimate their existence).

If you think such a proposal has even the slightest chance of being
adopted, you're fooling yourself.  Such a change would invalidate an
enormous amount of code, and I don't think a mechanical translator could
convert such code to whatever new style you're proposing.

Quote:
>As a matter of facts, many points appear not to have been cleaned by
>the standard committee

You seem to misunderstand the charter of X3J13.  We didn't set out to
design a new, "clean" language.  Our purpose was to clarify and
disambiguate the language defined in CLtL, and add an object-oriented
facility, condition handling, better iteration, and a window interface (we
eventually dropped this last one).  The main goal of a language standard is
portability, not design purity; that's for the academicians (that's why
Scheme exists).  We did make some incompatible changes for esthetic
reasons, but they were mostly things like changing names of things to be
consistent, and converters and compatibility packages for these kinds of
changes are relatively straightforward to implement.  We also made some
incompatible changes (e.g. to PROVIDE and REQUIRE) where the language
defined by CLtL was simply impossible to implement portably.
--
Barry Margolin, Thinking Machines Corp.


{uunet,harvard}!think!barmar



Tue, 02 Aug 1994 16:22:11 GMT  
 &rest and &key

   Almost anything can be seen as an environment issue.  Why try to
   give a langauge a reasonable syntax at all?  Why not let the
   environment provide the readable syntax?

Actually, one of the interesting things about Lisp is that in a way
this is already true.  The reader/prettyprinter provide (much of) the
external syntax, and can be seen as part of the "environment", taking
the environment to include the standard available programs and
libraries, just as awk, grep, et al are part of the Unix
"environment".  

The existence of this parser/unparser in the environment helps make
the use of "embedded languages" so natural in Lisp, just as the
analogous (but less convenient) facility provided by lexx/yacc in unix
encourages (but not as strongly) the use of "little languages" in Unix.

(An embedded language is a language for some very special purpose,
e.g. the commands in Interlisp for specifying what whould be printed
in what order on a source-code file.  Such a language acts as a
specialized sub-language extending lisp, and is implemented by a
simple interpretter written in lisp.  A "little language" is the same
idea, implemented as a compiler written using lexx/yacc or the
equivalent, and is an extension to the Unix environment - programs
written in the language are compiled and can be used with other unix
programs via pipes.)

In fact, the style of AI research that involves designing a specialize
"AI" language (e.g. planner) and using that language to write problem
solvers in was probably also encouraged by the availability of this
external <-> internal syntax conversion.
--
                                        Lou Steinberg

uucp:   {pretty much any major site}!rutgers!aramis.rutgers.edu!lou



Tue, 02 Aug 1994 23:05:11 GMT  
 &rest and &key
   Date: 14 Feb 92 15:05:11 GMT

   The existence of this parser/unparser in the environment helps make
   the use of "embedded languages" so natural in Lisp, just as the
   analogous (but less convenient) facility provided by lexx/yacc in unix
   encourages (but not as strongly) the use of "little languages" in Unix.

Any time you write a macro you're extending the syntax of the
language.  Most lisp programs end up being of the form "define a set
of primitives for describing my domain; implement my program in them."
This is so natural that even using the unix-inspired name "little
languages" seems like overkill.

It's funny, and sort of sad, that the folks in the c/unix domain have
to stand on their head so often.  In that environment, even a "litttle
language" is a big deal.



Tue, 02 Aug 1994 15:54:14 GMT  
 &rest and &key

[while discussing doing your own keyword processing]

 >And it's a pain to implement the rule that a keyword may appear multiple
 >times in an argument list but only the rightmost one has an effect and the
 >rest should be ignored ...

Actually, it's the leftmost keyword that is used (CLtL2, p. 80).  This
rule is easy to implement using (for example) GETF.

Kevin Gallagher



Wed, 03 Aug 1994 01:40:12 GMT  
 
 [ 36 post ]  Go to page: [1] [2] [3]

 Relevant Pages 

1. &rest and &key

2. Removing &key's from &rest

3. Any way to get &optional or &rest in Scheme

4. difference between &rest and &body

5. &optional with &key

6. Macro lambda lists: &key and &body

7. ABC Templates @#$%^&*&&*&*!!!!!!

8. non-list "&rest"

9. macros, &REST and REMF

10. &REST in VALUES type specifier

11. Re-raveling &REST idiom

12. Calling a function with your &rest

 

 
Powered by phpBB® Forum Software