From: Gregory Novak
Subject: Parameter List Style Question
Date: 
Message-ID: <m2psj9fs09.fsf@ucolick.org>
I was a little surprised by what Lisp does when you have both &rest
and &key parameters in a lambda list, and I have to say that I like
Python's way of doing it better (ie, once a keyword parameter "finds"
a binding, it's removed from the **kw dictionary).  

That is, I often find myself writing functions like this:

(defun lower-level-function (&key (one 'default))
       ... )

(defun higher-level-function (&rest rest &key two)
  (apply #'lower-level-function rest)

I'd like to be able to write 
(higher-level-function :one 'foo :two 'bar) 
and have higher-level-function see :two and lower-level-function see
:one (but not :two).  In Python this is what happens but in Lisp
lower-level-function will see both keyword variables.

So it seems that my choice is to:

1) Forget about &rest and explicitly pass the keyword parameters
through the call stack.  Ie:
(defun lower-level-function (&key (one 'default))
       ... )
(defun higher-level-function (&key (one 'default) two)
  (lower-level-function :one one)

But this more or less makes the keyword parameters into required
parameters since I'm specifying them everywhere.  Also I have to
duplicate all of my default values everywhere (or possibly have lots
of tests of supplied-p values).

2) Hack up the rest parameter to remove variables when I use them

3) use &allowed-other-keys everywhere, essentially turning off keyword
argument testing.  That is, I'd need something like this:

(defun lower-level-function (&key (one 'default))
       ... )

(defun higher-level-function (&rest rest &key two &allow-other-keys)
  (apply #'lower-level-function rest &allow-other-keys t)

But then if I type (higher-level-function :one 'foo :typo 'bar), no
one will notice (correct?).

Is there a clean way to handle this?  What do other people do?

Thanks,
Greg

From: Barry Margolin
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <barmar-F15945.14584122042006@comcast.dca.giganews.com>
In article <··············@ucolick.org>,
 Gregory Novak <·····@ucolick.org> wrote:

> I was a little surprised by what Lisp does when you have both &rest
> and &key parameters in a lambda list, and I have to say that I like
> Python's way of doing it better (ie, once a keyword parameter "finds"
> a binding, it's removed from the **kw dictionary).  
> 
> That is, I often find myself writing functions like this:
> 
> (defun lower-level-function (&key (one 'default))
>        ... )
> 
> (defun higher-level-function (&rest rest &key two)
>   (apply #'lower-level-function rest)
> 
> I'd like to be able to write 
> (higher-level-function :one 'foo :two 'bar) 
> and have higher-level-function see :two and lower-level-function see
> :one (but not :two).  In Python this is what happens but in Lisp
> lower-level-function will see both keyword variables.

This is intentional.  The intent was that often the wrapper function 
wants to look at some of the parameters that are being passed to the 
inner function, but not modify them along the way.

> 3) use &allowed-other-keys everywhere, essentially turning off keyword
> argument testing.  That is, I'd need something like this:
> 
> (defun lower-level-function (&key (one 'default))
>        ... )
> 
> (defun higher-level-function (&rest rest &key two &allow-other-keys)
>   (apply #'lower-level-function rest &allow-other-keys t)

That should be :ALLOW-OTHER-KEYS T, and yes, this is the expected way to 
do it.

> But then if I type (higher-level-function :one 'foo :typo 'bar), no
> one will notice (correct?).

True.  I guess this was considered a minor misfeature.
\

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Rob Warnock
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <7dGdnc0aJK4rRtfZnZ2dneKdnZydnZ2d@speakeasy.net>
Barry Margolin  <······@alum.mit.edu> wrote:
+---------------
|  Gregory Novak <·····@ucolick.org> wrote:
| > (defun higher-level-function (&rest rest &key two &allow-other-keys)
| >   (apply #'lower-level-function rest &allow-other-keys t)
| 
| That should be :ALLOW-OTHER-KEYS T, and yes, this is the expected way
| to do it.
+---------------

Having made this very mistake myself, I feel reasonably entitled  ;-}
to add that the REST arg [a list] must go last in the call to APPLY:

    (defun higher-level-function (&rest rest &key two &allow-other-keys)
      ...[do something with TWO]...
      (apply #'lower-level-function :allow-other-keys t rest))


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Rainer Joswig
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <joswig-138706.19335022042006@news-europe.giganews.com>
In article <··············@ucolick.org>,
 Gregory Novak <·····@ucolick.org> wrote:

> I was a little surprised by what Lisp does when you have both &rest
> and &key parameters in a lambda list, and I have to say that I like
> Python's way of doing it better (ie, once a keyword parameter "finds"
> a binding, it's removed from the **kw dictionary).  
> 
> That is, I often find myself writing functions like this:
> 
> (defun lower-level-function (&key (one 'default))
>        ... )
> 
> (defun higher-level-function (&rest rest &key two)
>   (apply #'lower-level-function rest)
> 
> I'd like to be able to write 
> (higher-level-function :one 'foo :two 'bar) 
> and have higher-level-function see :two and lower-level-function see
> :one (but not :two).  In Python this is what happens but in Lisp
> lower-level-function will see both keyword variables.
> 
> So it seems that my choice is to:
> 
> 1) Forget about &rest and explicitly pass the keyword parameters
> through the call stack.  Ie:
> (defun lower-level-function (&key (one 'default))
>        ... )
> (defun higher-level-function (&key (one 'default) two)
>   (lower-level-function :one one)
> 
> But this more or less makes the keyword parameters into required
> parameters since I'm specifying them everywhere.  Also I have to
> duplicate all of my default values everywhere (or possibly have lots
> of tests of supplied-p values).
> 
> 2) Hack up the rest parameter to remove variables when I use them

Symbolics Genera provides SI::REM-KEYWORDS and
SI::WITH-REM-KEYWORDS. The old CLIM implementation
then has in its Lisp utilities library
REMOVE-KEYWORDS ;-) and WITH-KEYWORDS-REMOVED.

-- 
http://lispm.dyndns.org/
From: Pascal Costanza
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <4avjj9Fure51U1@individual.net>
Rainer Joswig wrote:
> In article <··············@ucolick.org>,
>  Gregory Novak <·····@ucolick.org> wrote:
> 
>> I was a little surprised by what Lisp does when you have both &rest
>> and &key parameters in a lambda list, and I have to say that I like
>> Python's way of doing it better (ie, once a keyword parameter "finds"
>> a binding, it's removed from the **kw dictionary).  
>>
>> That is, I often find myself writing functions like this:
>>
>> (defun lower-level-function (&key (one 'default))
>>        ... )
>>
>> (defun higher-level-function (&rest rest &key two)
>>   (apply #'lower-level-function rest)
>>
>> I'd like to be able to write 
>> (higher-level-function :one 'foo :two 'bar) 
>> and have higher-level-function see :two and lower-level-function see
>> :one (but not :two).  In Python this is what happens but in Lisp
>> lower-level-function will see both keyword variables.
>>
>> So it seems that my choice is to:
>>
>> 1) Forget about &rest and explicitly pass the keyword parameters
>> through the call stack.  Ie:
>> (defun lower-level-function (&key (one 'default))
>>        ... )
>> (defun higher-level-function (&key (one 'default) two)
>>   (lower-level-function :one one)
>>
>> But this more or less makes the keyword parameters into required
>> parameters since I'm specifying them everywhere.  Also I have to
>> duplicate all of my default values everywhere (or possibly have lots
>> of tests of supplied-p values).
>>
>> 2) Hack up the rest parameter to remove variables when I use them
> 
> Symbolics Genera provides SI::REM-KEYWORDS and
> SI::WITH-REM-KEYWORDS. The old CLIM implementation
> then has in its Lisp utilities library
> REMOVE-KEYWORDS ;-) and WITH-KEYWORDS-REMOVED.

The idiom I typically use is this:

(defun high-level-function (&rest args &key ...)
   (declare (dynamic-extent args))
   ...
   (apply #'lower-level-function
          (loop for (key value) on args by #'cddr
                unless (member key keys-i-dont-want)
                collect `(,key ,value))))

I find this so straightforward that I don't see a need for a dedicated 
function here.

Don't forget the dynamic-extent declaration. You typically want it for 
&rest arguments in functions.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Rainer Joswig
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <joswig-5024D0.23315022042006@news-europe.giganews.com>
In article <··············@individual.net>,
 Pascal Costanza <··@p-cos.net> wrote:

> Rainer Joswig wrote:
> > In article <··············@ucolick.org>,
> >  Gregory Novak <·····@ucolick.org> wrote:
> > 
> >> I was a little surprised by what Lisp does when you have both &rest
> >> and &key parameters in a lambda list, and I have to say that I like
> >> Python's way of doing it better (ie, once a keyword parameter "finds"
> >> a binding, it's removed from the **kw dictionary).  
> >>
> >> That is, I often find myself writing functions like this:
> >>
> >> (defun lower-level-function (&key (one 'default))
> >>        ... )
> >>
> >> (defun higher-level-function (&rest rest &key two)
> >>   (apply #'lower-level-function rest)
> >>
> >> I'd like to be able to write 
> >> (higher-level-function :one 'foo :two 'bar) 
> >> and have higher-level-function see :two and lower-level-function see
> >> :one (but not :two).  In Python this is what happens but in Lisp
> >> lower-level-function will see both keyword variables.
> >>
> >> So it seems that my choice is to:
> >>
> >> 1) Forget about &rest and explicitly pass the keyword parameters
> >> through the call stack.  Ie:
> >> (defun lower-level-function (&key (one 'default))
> >>        ... )
> >> (defun higher-level-function (&key (one 'default) two)
> >>   (lower-level-function :one one)
> >>
> >> But this more or less makes the keyword parameters into required
> >> parameters since I'm specifying them everywhere.  Also I have to
> >> duplicate all of my default values everywhere (or possibly have lots
> >> of tests of supplied-p values).
> >>
> >> 2) Hack up the rest parameter to remove variables when I use them
> > 
> > Symbolics Genera provides SI::REM-KEYWORDS and
> > SI::WITH-REM-KEYWORDS. The old CLIM implementation
> > then has in its Lisp utilities library
> > REMOVE-KEYWORDS ;-) and WITH-KEYWORDS-REMOVED.
> 
> The idiom I typically use is this:
> 
> (defun high-level-function (&rest args &key ...)
>    (declare (dynamic-extent args))
>    ...
>    (apply #'lower-level-function
>           (loop for (key value) on args by #'cddr
>                 unless (member key keys-i-dont-want)
>                 collect `(,key ,value))))
> 
> I find this so straightforward that I don't see a need for a dedicated 
> function here.

He, you copy the arg-list even there may not be any keys
you need to remove. The Symbolics version also makes
sure that the new arglist is efficiently allocated using
internal data stack hackery.

You don't really like your version better than this?

(defun high-level-function (&rest args &key ...)
  (utils:with-keywords-removed  (args1 args (:key1))
     (apply #'lower-level-function args1)))

I can have documentation (which in any self-respecting Lisp is only
a keystroke away) on the macro and already the macro is
almost self-documenting. Writing the macro in any better
Lisp is just "w-k-r complete" - instead of typing the whole
LOOP...

-- 
http://lispm.dyndns.org/
From: Pascal Costanza
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <4avmoaFv1eoaU1@individual.net>
Rainer Joswig wrote:
> In article <··············@individual.net>,
>  Pascal Costanza <··@p-cos.net> wrote:
> 
>> The idiom I typically use is this:
>>
>> (defun high-level-function (&rest args &key ...)
>>    (declare (dynamic-extent args))
>>    ...
>>    (apply #'lower-level-function
>>           (loop for (key value) on args by #'cddr
>>                 unless (member key keys-i-dont-want)
>>                 collect `(,key ,value))))
>>
>> I find this so straightforward that I don't see a need for a dedicated 
>> function here.
> 
> He, you copy the arg-list even there may not be any keys
> you need to remove. The Symbolics version also makes
> sure that the new arglist is efficiently allocated using
> internal data stack hackery.

...on the other hand, my code makes only one pass through the rest-list. ;)

> You don't really like your version better than this?
> 
> (defun high-level-function (&rest args &key ...)
>   (utils:with-keywords-removed  (args1 args (:key1))
>      (apply #'lower-level-function args1)))
> 
> I can have documentation (which in any self-respecting Lisp is only
> a keystroke away) on the macro and already the macro is
> almost self-documenting. Writing the macro in any better
> Lisp is just "w-k-r complete" - instead of typing the whole
> LOOP...

I use this rarely, so I don't really bother that much. I also have code 
where the processing of the list of keyword arguments is somewhat more 
complex. It's easier to switch between the different variants this way.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Pascal Costanza
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <4avmh3Fv71rbU1@individual.net>
Pascal Costanza wrote:

> The idiom I typically use is this:
> 
> (defun high-level-function (&rest args &key ...)
>   (declare (dynamic-extent args))
>   ...
>   (apply #'lower-level-function
>          (loop for (key value) on args by #'cddr
>                unless (member key keys-i-dont-want)
>                collect `(,key ,value))))

Typo: This should be nconc instead of collect.

Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Rainer Joswig
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <joswig-D24176.23520722042006@news-europe.giganews.com>
In article <··············@individual.net>,
 Pascal Costanza <··@p-cos.net> wrote:

> Pascal Costanza wrote:
> 
> > The idiom I typically use is this:
> > 
> > (defun high-level-function (&rest args &key ...)
> >   (declare (dynamic-extent args))
> >   ...
> >   (apply #'lower-level-function
> >          (loop for (key value) on args by #'cddr
> >                unless (member key keys-i-dont-want)
> >                collect `(,key ,value))))
> 
> Typo: This should be nconc instead of collect.

Now you need to change all your sources... ;-)

> 
> Pascal

-- 
http://lispm.dyndns.org/
From: Pascal Costanza
Subject: Re: Parameter List Style Question
Date: 
Message-ID: <4avnmfFvatmnU4@individual.net>
Rainer Joswig wrote:
> In article <··············@individual.net>,
>  Pascal Costanza <··@p-cos.net> wrote:
> 
>> Pascal Costanza wrote:
>>
>>> The idiom I typically use is this:
>>>
>>> (defun high-level-function (&rest args &key ...)
>>>   (declare (dynamic-extent args))
>>>   ...
>>>   (apply #'lower-level-function
>>>          (loop for (key value) on args by #'cddr
>>>                unless (member key keys-i-dont-want)
>>>                collect `(,key ,value))))
>> Typo: This should be nconc instead of collect.
> 
> Now you need to change all your sources... ;-)

No, I don't because they are correct. ;)


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/