From: Tim Moore
Subject: style in altering keyword argument lists
Date: 
Message-ID: <a1d56s$1q3$0@216.39.145.192>
Here's a style question for y'all:

In many Lisp APIs it's common to specify a function -- usually taking a
bunch of keyword arguments -- that turns around and calls a generic
function which takes one of those keyword args as a required argument.
For example:

(defun accept (type &rest args &key (stream *standard-input*) ...))

(defgeneric stream-accept (stream type &key ...))

So, how do you go about massaging args so it can be passed to
stream-accept?  The options as I see them:

1)
(remf args :stream) 
(apply #'stream-accept stream type args)
and damn the consequences;

2)
(setf args (copy-list args))
(remf args :stream)
(apply #'stream-accept stream type args)

3)
(apply #'stream-accept stream type  :allow-other-keys t args)

4) Put &allow-other-keys in stream-accept's lambda list.

What would you do?
Tim

From: Thomas F. Burdick
Subject: Re: style in altering keyword argument lists
Date: 
Message-ID: <xcvofk5lse4.fsf@conquest.OCF.Berkeley.EDU>
"Tim Moore" <·····@bricoworks.com> writes:

> Here's a style question for y'all:
> 
> In many Lisp APIs it's common to specify a function -- usually taking a
> bunch of keyword arguments -- that turns around and calls a generic
> function which takes one of those keyword args as a required argument.
> For example:
> 
> (defun accept (type &rest args &key (stream *standard-input*) ...))
> 
> (defgeneric stream-accept (stream type &key ...))
> 
> So, how do you go about massaging args so it can be passed to
> stream-accept?  The options as I see them:
> 
> 1)
> (remf args :stream) 
> (apply #'stream-accept stream type args)
> and damn the consequences;

I'm not sure the phrase "damn the consequences" indicates good style :)

> 2)
> (setf args (copy-list args))
> (remf args :stream)
> (apply #'stream-accept stream type args)

I do almost this:

  (defun remove-property (prop list)
    (loop for name in list by #'cddr
          for val in (rest list) by #'cddr
          unless (eql prop name)
            collect name and collect val))

  (apply #'stream-accept stream type (remove-property :stream args))

I also have remove-property-if, in case I have more than one to
remove.  I know it's rarely a big deal, but I avoid traversing lists
more than once if I can help it.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Arthur Lemmens
Subject: Re: style in altering keyword argument lists
Date: 
Message-ID: <3C3ABB14.F7C9840E@xs4all.nl>
"Thomas F. Burdick" wrote:

>     (loop for name in list by #'cddr
>           for val in (rest list) by #'cddr

I prefer writing that as:

     (loop for (name val) on list by #'cddr

--
Arthur Lemmens
From: Kaz Kylheku
Subject: Re: style in altering keyword argument lists
Date: 
Message-ID: <kBp_7.290$Mc5.26583@news3.calgary.shaw.ca>
In article <············@216.39.145.192>, Tim Moore wrote:
>(defun accept (type &rest args &key (stream *standard-input*) ...))
>
>(defgeneric stream-accept (stream type &key ...))
>
>So, how do you go about massaging args so it can be passed to
>stream-accept?  The options as I see them:

Do you really want that? That is to say, do you want keyword parameters
to travel transparently through accept down to stream-accept?

One possibility you have omitted is to support, in your wrapper, every
keyword argument known to stream-accept, and explicitly pass them down.

>1)
>(remf args :stream) 
>(apply #'stream-accept stream type args)
>and damn the consequences;

If you damn these consequences, then Lisp may damn your program. :)

The HyperSpec says, under APPLY: ``Because a function can neither detect
whether it was called via apply nor whether (if so) the last argument
to apply was a constant, conforming programs must neither rely on the
list structure of a rest list to be freshly consed, nor modify that
list structure.'' 

>2)
>(setf args (copy-list args))
>(remf args :stream)
>(apply #'stream-accept stream type args)
>
>3)
>(apply #'stream-accept stream type  :allow-other-keys t args)
>
>4) Put &allow-other-keys in stream-accept's lambda list.

Solution 2 which removes the keyword is cleanest, because
it achieves the greatest transparency, without forcing the target
function to allow-other-keys. ``Pluck what you recognize, pass through
the rest'' seems like a sound principle.

The problem with 3 and 4 is that they only shut up the language
implementation by forcing the function to take the unknown key.
That is only one problem. 

What if one day that unknown key is no longer an unknown key because
someone adds it to the generic function? Then it's suddenly inappropriate
to pass it through, because it will be misinterpreted.
From: Tim Moore
Subject: Re: style in altering keyword argument lists
Date: 
Message-ID: <a1db67$i3p$0@216.39.145.192>
In article <···················@news3.calgary.shaw.ca>, "Kaz Kylheku"
<···@accton.shaw.ca> wrote:


> In article <············@216.39.145.192>, Tim Moore wrote:
>>(defun accept (type &rest args &key (stream *standard-input*) ...)) 
>>(defgeneric stream-accept (stream type &key ...)) 
>> So, how do you go
>>about massaging args so it can be passed to stream-accept?  The options
>>as I see them:
> Do you really want that? That is to say, do you want keyword parameters
> to travel transparently through accept down to stream-accept?  
Yes.

>One
> possibility you have omitted is to support, in your wrapper, every
> keyword argument known to stream-accept, and explicitly pass them down.

Right, but that has the problem of precluding different defaults in the
various methods methods of stream-accept.

Tim
From: Barry Margolin
Subject: Re: style in altering keyword argument lists
Date: 
Message-ID: <5wE_7.3$Kg.22128@burlma1-snr2>
In article <············@216.39.145.192>,
Tim Moore <·····@bricoworks.com> wrote:
>In article <···················@news3.calgary.shaw.ca>, "Kaz Kylheku"
><···@accton.shaw.ca> wrote:
>>One
>> possibility you have omitted is to support, in your wrapper, every
>> keyword argument known to stream-accept, and explicitly pass them down.
>
>Right, but that has the problem of precluding different defaults in the
>various methods methods of stream-accept.

Also, if the developer of the function you call adds new keyword options,
you would like not to have to update the wrapper functions to know about
them.  They should inherit the functionality automatically.

Or consider writing a function that calls OPEN, or a macro that expands
into a use of WITH-OPEN-FILE.  Many CL implementations have
implementation-specific keyword options that can be used in both of these.
What you would often like is to be able to accept all OPEN options,
including implementation-specific extensions.  If you had to enumerate all
the options to your function/macro, it would be difficult to write it in a
portable way.

-- 
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kent M Pitman
Subject: Re: style in altering keyword argument lists
Date: 
Message-ID: <sfw666dlloc.fsf@shell01.TheWorld.com>
···@accton.shaw.ca (Kaz Kylheku) writes:

[Re: :allow-other-keys]

> What if one day that unknown key is no longer an unknown key because
> someone adds it to the generic function? Then it's suddenly inappropriate
> to pass it through, because it will be misinterpreted.

This is not as legitimate a concern as it sounds.  There are a million
non-syntactic possible problems caused by changes in the language.
The fact is that the langauge simply must not be changed casually
eactly because code relying on this might be broken.

It's fine to make your code worry about this, just as it's fine to
worry about all kinds of other things people worry about --like not
using things that might side-effect or not using things that might
cons.  Just don't confuse that with "problems introduced by the
language".  Those are problems introduced by one's own mind.
Just as real perhaps.  But of a different causation.