From: Drew McDermott
Subject: Evaluated keyword arg markers
Date: 
Message-ID: <3AA90A37.DEC76A7D@yale.edu>
In theory, the keywords associated with Common Lisp &key args are
ordinary argument positions.  When I write

(foo :arg (+ x y) :another 'obscure)

:arg and :another are evaluated, just as (+ x y) and 'obscure are.  They
just happen to evaluate to themselves.  So I could write code such as

(let ((key (cond ((hairy test) ':another) (t ':alt))))
   (foo :arg (+ x y) key 'obscure))

where foo is perhaps defined as

(defun foo (&key arg another alt) ...)

On some occasions another is bound to obscure and alt to nil, and on
other occasions alt is bound to obscure and another to nil.

Query: Has anyone ever made use of this ability?  I have trouble
visualizing a case where I would want to.

This is basically a matter of idle curiosity, but one could argue that
keywords are better thought of as argument markers than actual
arguments.  Being able to compute the markers at run time is analogous
to being able to reorder required or optional arguments at run time.

   -- Drew McDermott

From: Joe Marshall
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <r906vp42.fsf@content-integrity.com>
Drew McDermott <··············@yale.edu> writes:


> Query: Has anyone ever made use of this ability? [The ability to
> use computed keywords]

I have seen it.

> I have trouble visualizing a case where I would want to.

I can envision two cases.  The common one is when you have an &rest arg
and want to pass it to a function that takes keywords.  The keywords
are therefore computed (although likely to be static in the location
they come from).

I can also imagine someone trying to be clever with the :start and
:end arguments of a sequence function.

> This is basically a matter of idle curiosity, but one could argue that
> keywords are better thought of as argument markers than actual
> arguments.  

Where the keywords are explicit at the call site, yes.


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----==  Over 80,000 Newsgroups - 16 Different Servers! =-----
From: Tim Bradshaw
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <ey3k85ylv0y.fsf@cley.com>
* Drew McDermott wrote:
> Query: Has anyone ever made use of this ability?  I have trouble
> visualizing a case where I would want to.

I use something pretty like this.  I have a terrible bloated function
which basically computes a whole bunch of useful things and needs some
sufficient set of keywords to do this.  I'm getting from elsewhere
some stuff which should tell me enough to call this function but is
all in some completely random order and needs work to massage out
stuff.  So I end up doing

        (apply #'terrible-bloated-function a1 a2 
               (compute-hairy-keyword-list-from-data
                awful-data-that-needs-massaged))

Actually I have to admit that an earlier version of this code was even
worse, it did this:

        (apply #'terrible-bloated-function a1 a2 
               (nreverse (compute-hairy-keyword-list-from-data
                          awful-data-that-needs-massaged)))

because COMPUTE-HAIRY-KEYWORD-LIST-FROM-DATA was less smart then and
could sometimes come back with multiple values (not as in
MULTIPLE-VALUE-x) for the KW list, of which the last was generally the
right one.  Unfortunately it turned out to not always be the right
one, so I made it work better.

OK, it's not quite the same thing as your case, but I think it's
reasonably close in some sense.

--tim
From: Kent M Pitman
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <sfwn1auzuav.fsf@world.std.com>
Drew McDermott <··············@yale.edu> writes:

> In theory, the keywords associated with Common Lisp &key args are
> ordinary argument positions.  When I write
> 
> (foo :arg (+ x y) :another 'obscure)
> 
> :arg and :another are evaluated, just as (+ x y) and 'obscure are.  They
> just happen to evaluate to themselves.  So I could write code such as
> 
> (let ((key (cond ((hairy test) ':another) (t ':alt))))
>    (foo :arg (+ x y) key 'obscure))
> 
> where foo is perhaps defined as
> 
> (defun foo (&key arg another alt) ...)
> 
> On some occasions another is bound to obscure and alt to nil, and on
> other occasions alt is bound to obscure and another to nil.
> 
> Query: Has anyone ever made use of this ability?  I have trouble
> visualizing a case where I would want to.

You're right that it's not typically used in the pattern you suggest;
it is, though, done "all the time" (for varying values of "all the time",
time being a multi-dimensional space in program-design) in other
patterns.  One of several such examples is:

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

Here we're doing surgery on which args being included, and in effect 
using them as regular positional args.

I think the reason we don't usually use them "variably" is that they are
like "prepositions" in English, and we don't usually use variable 
prepositions either--they are part of the language glue.  Suppose block A
is on top of B if A' is on top of B' and that otherwise (if B' is on A'),
B is on A, you do not have any way in English of saying "Let relX = relation
of B' to A', and assert B relX A." 

> This is basically a matter of idle curiosity, but one could argue that
> keywords are better thought of as argument markers than actual
> arguments.  Being able to compute the markers at run time is analogous
> to being able to reorder required or optional arguments at run time.

I certainly have long believed this.  And, btw, nothing keeps
implementations from doing a big optimization that as far as I know
most or all implementations don't do of having multiple entry points
into a function similar to the way number-compiled functions used to
have in Maclisp.  You need one such entry point per argument
configuration, and the linker can make more as new patterns are
loaded.  So keycalls could be done as positional calls by functions
that knew such entry points would be available.  (Loaders could even
patch up for functions that were miscompiled under improper assumptions.)

I think keyword calling was an experiment when it went into CL and we didn't
know how people would use it.  We now have a lot of data and it's ripe
for some rethinking to see if some better abstractions and/or
more efficient implementations are in order.
From: Barry Margolin
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <%H9q6.34$%h5.21195@burlma1-snr2>
In article <···············@world.std.com>,
Kent M Pitman  <······@world.std.com> wrote:
>I think the reason we don't usually use them "variably" is that they are
>like "prepositions" in English, and we don't usually use variable 
>prepositions either--they are part of the language glue.

I agree with this.  About the only situation I can think of where a
variable keyword might be useful is with functions that take :TEST and
:TEST-NOT arguments.  I could imagine a function like:

(defun do-something (... invertp ...)
  ...
  (some-sequence-function ...
    (if invertp :test-not :test) #'predicate
    ...)
  ...)

Basically, selecting the keyword programmatically probably only makes sense
when there are several related options that you need to choose from at run
time.  :TEST and :TEST-NOT are closely related, but I can't think of any
other functions that have option sets like that.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, 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: Barry Margolin
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <AGCq6.2$wM6.10857@burlma1-snr2>
In article <················@naggum.net>, Erik Naggum  <····@naggum.net> wrote:
>  If the keyword arguments to a function are all constant, it should be
>  possible to recognize this at compile-time and transform the call to a
>  function that takes only positional arguments.  They function that takes
>  keywords should then call this function.  The same can be said for
>  optional arguments.  It may make sense to do this automatically so the
>  function call takes more of the burden.  However, this is not a good idea
>  if you make changes to the lambda list without recompiling all calls,
>  which means that to _really_ support this, a system would have to keep
>  the old function around so it can call the next version of itself with
>  new arguments.  In other words, the machinery required to optimize this
>  in a dynamic environment like Common Lisp is probably worth a PhD.

If the caller and callee are in the same compilation unit, I think CL
allows the compiler to make a number of optimizations that assume the
callee's function binding and signature won't change, unless it is
explicitly proclaimed NOTINLINE.  This was done to permit block
compilation, and it should also allow a transformation like you describe
above.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, 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: Drew McDermott
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <3AB238B1.74207757@yale.edu>
Erik Naggum and several others wrote many things, including:

>   However, there is an underlying question to
>   your question that I think I see.  Let me know if I went out on the wrong
>   limb.  Are keyword arguments resolved in the caller or in the callee?

Actually, that wasn't the underlying question.  Nor was I confused about the
practice of computing keyword argument lists at run time and passing them to a
function using apply.  It seems to me that apply would work that way even if
keyword arguments weren't evaluated.

What I was really thinking about was how mapcar interacts with keyword
arguments.
Suppose I have a function foo with argument list (x y &key a b).  And suppose I
have 4 lists of arguments.  Under the "keyword as arg markers" semantics, it
seems to me that you could write

(mapcar #'foo x-list y-list :b b-list :a a-list)

In other words, mapcar would apply foo to cross sections of the lists, where
the first two are identified positionally and the last two by keyword (in
either order).

In fact, to get the desired behavior under the existing "keywords as arguments"
semantics you have to write something like

(mapcar #'foo x-list y-list (key-clone :b b-list) b-list (key-clone :a a-list)
a-list)

where key-clone is defined as

(defun key-clone (k l) (mapcar #'(lambda (x) (declare (ignore x)) k) l))

The ugliness of this probably has kept anyone from ever doing it (but I'd be
interested to hear of counterexamples).

Thanks to Barry Margolin for giving a nontrivial example of evaluated keywords:

>>>About the only situation I can think of where a
variable keyword might be useful is with functions that take :TEST and
:TEST-NOT arguments.  I could imagine a function like:

(defun do-something (... invertp ...)
  ...
  (some-sequence-function ...
    (if invertp :test-not :test) #'predicate
    ...)
  ...)
<<<

Of course, you could also write this as

(defun do-something (...invertp...)
   ...
   (some-sequence-function ....
      :test (if invertp (negate #'predicate) #'predicate)
      ...)
  ...)

where negate is of course (lambda (p) #'(lambda (x) (not (p x))).  (This is
probably a tad less efficient than the keyword hack, although not if the
compiler is clever enough.)

           -- Drew McDermott
From: Kent M Pitman
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <sfwwv9p1taw.fsf@world.std.com>
Drew McDermott <··············@yale.edu> writes:

> (defun do-something (...invertp...)
>    ...
>    (some-sequence-function ....
>       :test (if invertp (negate #'predicate) #'predicate)

NEGATE is spelled "COMPLEMENT" in ANSI CL.

You can also write:

(some-sequence-function 
   :test (funcall (if invertp #'complement #'identity) #'predicate)
   ...)

Though if you knew enough to name the predicate, you'd probably know enough
to name its complement.  So you probably secretly mean:

(some-sequence-function 
   :test (funcall (if invertp #'complement #'identity) predicate)
   ...)

- - - 
p.s. I though I elided the keyword vs mapcar observations, I thought
     they were quite interesting to think about.
From: Rahul Jain
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <98ue6o$1fq$1@joe.rice.edu>
In article <·················@yale.edu> on Fri, 16 Mar 2001 10:00:49
-0600, "Drew McDermott" <··············@yale.edu> wrote:

> (mapcar #'foo x-list y-list (key-clone :b b-list) b-list (key-clone :a
> a-list)
> a-list)
...
> The ugliness of this probably has kept anyone from ever doing it (but
> I'd be interested to hear of counterexamples).
> 

how about:

(mapcar #'foo x-list y-list
              #1=(:b . #1#) b-list
              #1=(:a . #1#) a-list)


Thanks to KMP for the inspiration for this solution in an unrelated
thread.

-- 
-> -/-                       - 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.
From: Kent M Pitman
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <sfwzoelgfmw.fsf@world.std.com>
"Rahul Jain" <·····@rice.edu> writes:

> In article <·················@yale.edu> on Fri, 16 Mar 2001 10:00:49
> -0600, "Drew McDermott" <··············@yale.edu> wrote:
> 
> > (mapcar #'foo x-list y-list (key-clone :b b-list) b-list (key-clone :a
> > a-list)
> > a-list)
> ...
> > The ugliness of this probably has kept anyone from ever doing it (but
> > I'd be interested to hear of counterexamples).
> 
> how about:
> 
> (mapcar #'foo x-list y-list
>               #1=(:b . #1#) b-list
>               #1=(:a . #1#) a-list)
> 

A couple of syntax errors there, but still "cute".  

(defun foo (x y &key a b) (cons (+ x y) (- a b)))

(mapcar #'foo 
        '(1 2 3)
        '(4 5 6) 
        '#1=(:a . #1#) '(7 8 9)
        '#2=(:b . #2#) '(10 11 12))
=> ((5 . -3) (7 . -3) (9 . -3))

- - - - 

The real issue, though, is that keyword calling is expensive and if you've
already figured out what the keyword args are, you don't need to do it
every time.  What you're not doing here (but could) is:

(mapcar #'foo
        '(1 2 3)
        '(4 5 6)
        '(:a :b :a) '(7 11 9)
        '(:b :a :b) '(10 8 12))
=> ((5 . -3) (7 . -3) (9 . -3))

And I took what Drew was saying to be that if you were passing a keyword
to mapcar, as in:

(mapcar #'foo
        '(1 2 3)
        '(4 5 6)
         :a '(7 8 9)
         :b '(10 8 12))

and then it was doing the keyword checking only once instead of per call,
there'd be a big speed gain.  I think he's right, though I think the big
benefit would come if people would just optimize keyword calls normally,
the optimization of mapcar being icing on the cake.  That is, the real
speed gain would come by just noticing that for every keyworded function
there are a finite number of argument call orders, and there could just be
a vector of trampoline functions to call through into a function of 
positional args in order to call the function.  

I *think* CLIM does this.  I know I went to a lot of effort to get compiler
optimizers squeezed into the standard at the last minute in hopes of enabling
users to take control of this kind of optimization when the system doesn't.
I guess it's probably not the case that compiler optimizers can be stretched
to work on higher order functions like this, since there's no really obvious
way to get a foothold (other than improperly writing one for a system 
function, which I don't think is allowed).
From: ········@cc.hut.fi
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <m3bsqzpel5.fsf@mu.nonexistent>
Kent M Pitman <······@world.std.com> writes:
> And I took what Drew was saying to be that if you were passing a keyword
> to mapcar, as in:
> 
> (mapcar #'foo
>         '(1 2 3)
>         '(4 5 6)
>          :a '(7 8 9)
>          :b '(10 8 12))
> 
> and then it was doing the keyword checking only once instead of per call,
> there'd be a big speed gain.  I think he's right, though I think the big

If there were any speed gains to be had here, shouldn't it be sufficient
to write the call like this:

(mapcar #'(lambda (x y a b)
            (foo x y :a a :b b))
         '(1 2 3) '(4 5 6) '(7 8 9) '(10 8 12))

I guess this could be seen as slightly more difficult to read though.

Hannu Rummukainen
From: Kalle Olavi Niemitalo
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <87elv8e757.fsf@PC486.y2000.kon.iki.fi>
Kent M Pitman <······@world.std.com> writes:

> (mapcar #'foo 
>         '(1 2 3)
>         '(4 5 6) 
>         '#1=(:a . #1#) '(7 8 9)
>         '#2=(:b . #2#) '(10 11 12))

Not conforming since MAPCAR requires proper lists.

(I had a feeling that something was wrong with the call -- if
_all_ of the list argunents were cyclic, MAPCAR would have to
cons forever which it can't do.  Today, I realized the standard
might explicitly forbid this case.  So it does, but in a stricter
way than I expected.)

Why are also MAPC and MAPL restricted this way?  
From: Tim Bradshaw
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <nkj7l0z4r7s.fsf@tfeb.org>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Not conforming since MAPCAR requires proper lists.
> 

Yes, but can you imagine an implementation that detects this?  I can,
but not one I'd like to use!

I think the no-improper-list restriction is more likely aimed at
dotted lists, which can be cheaply detected.

--tim
From: Kent M Pitman
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <sfwwv8zo5rp.fsf@world.std.com>
Tim Bradshaw <···@tfeb.org> writes:

> 
> Kalle Olavi Niemitalo <···@iki.fi> writes:
> 
> > Not conforming since MAPCAR requires proper lists.
> > 
> 
> Yes, but can you imagine an implementation that detects this?  I can,
> but not one I'd like to use!
> 
> I think the no-improper-list restriction is more likely aimed at
> dotted lists, which can be cheaply detected.

No, I think Kalle's got a point.  I've been a bad boy.

 (MAPCAR #'IDENTITY '#1=(X . #1#))

is a problem, and I'm pretty sure the spec definitely means to say this
is a no-no.  It would be well if the language definition in the future
were changed to say something like "The consequences are unspecified
if there is not at least one non-circular argument".  Given such a
restriction, one could lessen the type restriction on the arguments, but
not otherwise.
From: Barry Margolin
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <mx%y6.289$U4.10799@burlma1-snr2>
In article <···············@world.std.com>,
Kent M Pitman  <······@world.std.com> wrote:
>Tim Bradshaw <···@tfeb.org> writes:
>
>> 
>> Kalle Olavi Niemitalo <···@iki.fi> writes:
>> 
>> > Not conforming since MAPCAR requires proper lists.
>> > 
>> 
>> Yes, but can you imagine an implementation that detects this?  I can,
>> but not one I'd like to use!
>> 
>> I think the no-improper-list restriction is more likely aimed at
>> dotted lists, which can be cheaply detected.
>
>No, I think Kalle's got a point.  I've been a bad boy.
>
> (MAPCAR #'IDENTITY '#1=(X . #1#))
>
>is a problem, and I'm pretty sure the spec definitely means to say this
>is a no-no.  It would be well if the language definition in the future
>were changed to say something like "The consequences are unspecified
>if there is not at least one non-circular argument".  Given such a
>restriction, one could lessen the type restriction on the arguments, but
>not otherwise.

I haven't looked up the exact wording, but I believe it says that the
number of calls is the length of the shortest list.  So if all the lists
are circular or the shortest one is dotted, you're likely to run into
problems.

I guess the spec gives license for an implementation to check for arguments
that are dotted or circular after beyond the length of the shortest one,
but it would take an extremely perverse implementor to do so.  But
apparently no one considered the case of code that might depend on such
calls, so we didn't think to allow for them explicitly in the standard; it
was simpler to just say that the arguments must be proper lists.  If J13
were active, this is probably something that could be clarified in a Defect
Report or the next revision of the standard.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, 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: Evaluated keyword arg markers
Date: 
Message-ID: <sfwbsqb2xcb.fsf@world.std.com>
Barry Margolin <······@genuity.net> writes:

> I guess the spec gives license for an implementation to check for arguments
> that are dotted or circular after beyond the length of the shortest one,
> but it would take an extremely perverse implementor to do so.  

I think it's to allow for cases like:

 (mapcar #'cons '(a b . c) '(y z))

where you can sort of imagine some implementation that POP'd the first
argument (CAR'ing into the symbol C) before noticing the second argument
was shorter.  Rather than the standard quibble over whether such an 
implementation is correct, we just wrote wording that encourages you to
not rely on edge effects like this.  I can also imagine implementations 
wanting to do some sort of cache prefill/lookahead games that would rely 
only on knowing you had a valid list to cdr down doing the lookahead.
Who knows if an implementation would really do such things, but by making
restrictions of these kinds, we leave implementations some room to work
with... and we sometimes preclude reasonable uses on the user side.
From: Tim Bradshaw
Subject: Re: Evaluated keyword arg markers
Date: 
Message-ID: <nkjpuerie7m.fsf@tfeb.org>
Kent M Pitman <······@world.std.com> writes:

> 
> No, I think Kalle's got a point.  I've been a bad boy.
> 
>  (MAPCAR #'IDENTITY '#1=(X . #1#))
> 
> is a problem, and I'm pretty sure the spec definitely means to say this
> is a no-no. 

Yes, I was being imprecise.  What I meant was something like: although
the spec says that circular or dotted list aren't allowed, it's
unlikely, in my opinion, that any implementation will detect circular
lists, as checking is so expensive.  It's (again in my opinion)
slightly more likely that an implementation might detect a dotted cdr
in some part of the list that isn't reached by MAPCAR, but even that
would be a bit perverse.

> It would be well if the language definition in the future
> were changed to say something like "The consequences are unspecified
> if there is not at least one non-circular argument".  Given such a
> restriction, one could lessen the type restriction on the arguments, but
> not otherwise.

I agree with this, because something like this could be quite useful:

(defun make-defaulted-alist (keys)
   (mapcar #'cons keys '#1=(nil . #1#)))

(well, perhaps this isn't useful, but something like it might be).

--tim