From: Joshua Taylor
Subject: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <3c7b4e30-4304-4ea7-a304-08286a64a0ee@f40g2000pri.googlegroups.com>
Hi all,

I know that many CL functions take a :KEY argument for extracting
values from various kinds of objects, and that in many cases, if the
value given to :KEY is NIL, then the objects are just used by
themselves. E.g.,

(position #\a "ABCabc") => 3
(position #\a "ABCabc" :key nil) => 3
(position #\a "ABCabc" :key #'char-downcase) => 0

I'm pretty sure that I've read the section of the HyperSpec that
describes this behavior, but I can't find it again. The doc for ADJOIN
[2] says:

"key---a designator for a function of one argument, or nil.
…
The test, test-not, and key affect how it is determined whether item
is the same as an element of list. For details, see Section 17.2.1
(Satisfying a Two-Argument Test)."

And 17.2.1 and 17.2.2 [1] state that:

"The element Ei might not be considered directly. If a :key argument
is provided, it is a designator for a function of one argument to be
called with each Ei as an argument, and yielding an object Zi to be
used for comparison. (If there is no :key argument, Zi is Ei.) "

Now, I wouldn't have thought think that providing NIL as a :key
argument is quite the same as there being no :key argument, this
reading suggests that they are (at least for this purpose). Does some
other section state that "if there is no :key argument, or the :key
argument is NIL, Zi is Ei" ?

Pedantically,
//JT

[1] http://www.lispworks.com/documentation/HyperSpec/Body/17_ba.htm
and http://www.lispworks.com/documentation/HyperSpec/Body/17_bb.htm
[2] http://www.lispworks.com/documentation/HyperSpec/Body/f_adjoin.htm

From: TomSW
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <1dbe62e2-78dd-4ebe-aa06-aa372a351a22@x9g2000yqk.googlegroups.com>
On 6 Fev, 21:45, Joshua Taylor <···········@gmail.com> wrote:
> Hi all,
>
> I know that many CL functions take a :KEY argument for extracting
> values from various kinds of objects, and that in many cases, if the
> value given to :KEY is NIL, then the objects are just used by
> themselves. E.g.,

This is stated a bit more clearly in the 2nd edition of "Common Lisp
The Language" which talks about what happens when :key is nil, instead
of what happens "when there is no :key argument".

The alternatives are, when :key is supplied as nil: to raise an error
because nil is not a function, or to call nil on a list item anyway,
and let /that/ raise the error. It's very unlikely that the spec means
this, since a) nil is stated as a possible value of :key, b) the yuk
factor.

Your question seems odd to me. Are you reading the spec with a view to
implementing it, or were you passing an explicitly NIL :key in the
hope something else would happen?

cheers,
Tom
From: Rainer Joswig
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <joswig-3D4051.23481806022009@news-europe.giganews.com>
In article 
<····································@f40g2000pri.googlegroups.com>,
 Joshua Taylor <···········@gmail.com> wrote:

> Hi all,
> 
> I know that many CL functions take a :KEY argument for extracting
> values from various kinds of objects, and that in many cases, if the
> value given to :KEY is NIL, then the objects are just used by
> themselves. E.g.,
> 
> (position #\a "ABCabc") => 3
> (position #\a "ABCabc" :key nil) => 3
> (position #\a "ABCabc" :key #'char-downcase) => 0
> 
> I'm pretty sure that I've read the section of the HyperSpec that
> describes this behavior, but I can't find it again. The doc for ADJOIN
> [2] says:
> 
> "key---a designator for a function of one argument, or nil.
> �
> The test, test-not, and key affect how it is determined whether item
> is the same as an element of list. For details, see Section 17.2.1
> (Satisfying a Two-Argument Test)."
> 
> And 17.2.1 and 17.2.2 [1] state that:
> 
> "The element Ei might not be considered directly. If a :key argument
> is provided, it is a designator for a function of one argument to be
> called with each Ei as an argument, and yielding an object Zi to be
> used for comparison. (If there is no :key argument, Zi is Ei.) "
> 
> Now, I wouldn't have thought think that providing NIL as a :key
> argument is quite the same as there being no :key argument, this
> reading suggests that they are (at least for this purpose). Does some
> other section state that "if there is no :key argument, or the :key
> argument is NIL, Zi is Ei" ?
> 
> Pedantically,
> //JT
> 
> [1] http://www.lispworks.com/documentation/HyperSpec/Body/17_ba.htm
> and http://www.lispworks.com/documentation/HyperSpec/Body/17_bb.htm
> [2] http://www.lispworks.com/documentation/HyperSpec/Body/f_adjoin.htm


If you have a function like this:

(defun foo (&key bar)
  bar)

and you call it without arguments

 (foo)

Then BAR gets initialized to NIL by default. That's defined
in the ANSI CL standard.

So  (foo :bar nil)  and   (foo)  are the same.


You can define FOO also like this:

(defun foo (&key (bar nil bar-supplied-p))
  (values bar bar-supplied-p))


CL-USER 3 > (foo)
NIL
NIL

CL-USER 4 > (foo :bar nil)
NIL
T

Above you can detect a difference between the two calls.

But it would be kind of inconvenient if your
library depends on this in the general case.

Imagine:

(defun foo (item sequence &key key)
  (position item sequence :key key))
  
  Above you just pass the key value.

vs.

(defun foo (item sequence &key (key nil key-supplied-p))
   (if key-supplied-p
       (position item sequence :key key)
       (position item sequence)))

   Above you would need to check for each keyword argument
   if it is supplied - otherwise the library function might
   not be called as expected (because it could make a difference
   between passing NIL to a keyword argument and not
   providing the keyword argument).

The latter looks less elegant.


What I don't find in the ANSI CL spec is a sentences that says
what happens when you pass NIL to KEY in a function like
POSITION. The documentation to POSITION says that
KEY is either a function or NIL. But it kind of fails to say
that when you pass NIL, that it is the same as not
using the key argument KEY. I would think that it is
implied, but I failed to find it mentioned.

-- 
http://lispm.dyndns.org/
From: Barry Margolin
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <barmar-F68E28.22382106022009@mara100-84.onlink.net>
In article <····························@news-europe.giganews.com>,
 Rainer Joswig <······@lisp.de> wrote:

> If you have a function like this:
> 
> (defun foo (&key bar)
>   bar)
> 
> and you call it without arguments
> 
>  (foo)
> 
> Then BAR gets initialized to NIL by default. That's defined
> in the ANSI CL standard.

True, but that's not how I would expect most of these functions to be 
defined.  I'd expect them to be:

(defun foo (&key (key #'identity))
  ...)

since the default if no :key is supplied is to use the element itself.

-- 
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: ······@corporate-world.lisp.de
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <5e36c93d-4e12-438f-b6b5-865bfd1616df@q9g2000yqc.googlegroups.com>
On Feb 7, 4:38 am, Barry Margolin <······@alum.mit.edu> wrote:
> In article <····························@news-europe.giganews.com>,
>  Rainer Joswig <······@lisp.de> wrote:
>
> > If you have a function like this:
>
> > (defun foo (&key bar)
> >   bar)
>
> > and you call it without arguments
>
> >  (foo)
>
> > Then BAR gets initialized to NIL by default. That's defined
> > in the ANSI CL standard.
>
> True, but that's not how I would expect most of these functions to be
> defined.  I'd expect them to be:
>
> (defun foo (&key (key #'identity))
>   ...)
>
> since the default if no :key is supplied is to use the element itself.
>


Hmm, just looking at the MCL source. In many places it uses (&key key)
and calls (setf key (adjust-key key)) to set key to NIL
when key is #'identity or 'identity. Inside some function
then there is (if key (setf element (funcall key element))).
From: Olof-Joachim Frahm
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <87r62bnptx.fsf@straylight.rentiernetz>
Joshua Taylor <···········@gmail.com> writes:
> Now, I wouldn't have thought think that providing NIL as a :key
> argument is quite the same as there being no :key argument, this
> reading suggests that they are (at least for this purpose).

Well, compare it to how you create a function with keyword arguments,
where you either'll provide some default argument - which also can be
NIL, which equals providing no default.  But I can't cite something
from the Hyperspec (mostly because google doesn't like me, I think).

The one reason this is in a way interesting for me is the fact, that
the R language (or possibly also S), which I have to work with at the
moment, also provides keyword arguments (and optional and &rest
arguments for that matter) and there actually is a way to test for
not-provided _and_ provided-but-nil.  But again, it also has some
weird ways of constructing calls and so on, even if I don't know if
some library actually uses this kind of functionality.  Aaand it's
kind of ugly to actually have that functionality at your hands.

Okay, your question was more specific, but, why should it make a
difference if you provided an argument or used the default one?

Regards,
Olof

-- 
Es liegt in der menschlichen Natur, unvernünftig zu denken und zu handeln.
        -- nach Anatole France
From: Joshua Taylor
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <83d0dfa4-8e80-46fa-951d-765672a10e95@t3g2000yqa.googlegroups.com>
On Feb 6, 3:45 pm, Joshua Taylor <···········@gmail.com> wrote:
> Hi all,
>
> I know that many CL functions take a :KEY argument for extracting
> values from various kinds of objects, and that in many cases, if the
> value given to :KEY is NIL, then the objects are just used by
> themselves. E.g.,
>
> (position #\a "ABCabc") => 3
> (position #\a "ABCabc" :key nil) => 3
> (position #\a "ABCabc" :key #'char-downcase) => 0
>
> I'm pretty sure that I've read the section of the HyperSpec that
> describes this behavior, but I can't find it again. The doc for ADJOIN
> [2] says:
>
> "key---a designator for a function of one argument, or nil.
> …
> The test, test-not, and key affect how it is determined whether item
> is the same as an element of list. For details, see Section 17.2.1
> (Satisfying a Two-Argument Test)."
>
> And 17.2.1 and 17.2.2 [1] state that:
>
> "The element Ei might not be considered directly. If a :key argument
> is provided, it is a designator for a function of one argument to be
> called with each Ei as an argument, and yielding an object Zi to be
> used for comparison. (If there is no :key argument, Zi is Ei.) "
>
> Now, I wouldn't have thought think that providing NIL as a :key
> argument is quite the same as there being no :key argument, this
> reading suggests that they are (at least for this purpose). Does some
> other section state that "if there is no :key argument, or the :key
> argument is NIL, Zi is Ei" ?
>
> Pedantically,
> //JT
>
> [1]http://www.lispworks.com/documentation/HyperSpec/Body/17_ba.htm
> andhttp://www.lispworks.com/documentation/HyperSpec/Body/17_bb.htm
> [2]http://www.lispworks.com/documentation/HyperSpec/Body/f_adjoin.htm


I'm replying to my own message because there were a number of good
responses, and that it makes more sense to reply to them as a whole
rather than individually.

TomSW's reference to CLtL2 making this entire issue clearer is good. I
thought that this discussion came up a while ago, but I can't find it
via Google. At the time it may have been more related to scrubbing
argument lists for APPLY. (E.g., If CL didn't allow NIL as an
indicator that the object at hand should be used, if you write a
function that takes :KEY and :TEST arguments and will call a CL
function with them, then you either have to give them default values,
or, in order not to pass a value to the CL function, end up writing a
bunch of `(,@(when ,keyp `(:key ,key)) ,@(when ,testp `(:test ,test)))
sort of argument lists for APPLY. Since CL takes :KEY NIL to mean "use
the object itself", it's easier to APPLY, regardless of whether
someone gave you a :KEY or not since that default NIL is OK.) There is
a subtle difference between defaulting to #'identity or 'identity and
having no :KEY, though. 17.2.1 says "The object O might not be
compared directly to Ei. If a :key argument is provided, it is a
designator for a function of one argument to be called with each Ei as
an argument, and yielding an object Zi to be used for comparison. (If
there is no :key argument, Zi is Ei.)" LispWorks (5.1.2 on Intel Mac)
doesn't take that to mean that IDENTITY is called on every object, but
it doesn't optimize the call away, either:

CL-USER 20 > (trace identity)
(IDENTITY)

CL-USER 21 > (find 1 '(1 2 3))
1

CL-USER 22 > (find 1 '(1 2 3) :key 'identity)
;; If the key were called on every element, we'd see three calls here
0 IDENTITY > ...
  >> SYSTEM::X : 1
0 IDENTITY < ...
  << VALUE-0 : 1
1

Rainer Joswig points out that writing functions that take :KEY
arguments and pass them to CL functions is easier because NIL is
allowable as a provided key. This means that we can write functions
like (defun foo (&key key) (<CL-FUNCTION> :key key)). At the same
time, we have to provide a default test value if we want to write
(defun bar (&key key test) (<CL-FUNCTION> :key key :test test)) since
NIL ISN'T valid as a test argument, even though no default value
for :TEST is declared. 17.2.1 says "If neither a :test nor a :test-not
argument is supplied, it is as if a :test argument of #'eql was
supplied." If it said that "If neither a :test nor a :test-not
argument is supplied, __or both were NIL__, it is as if a :test
argument of #'eql was supplied." then it would be a little easier to
write functions that pass :TEST arguments through too.

The reason I ask is because I just happened to write such a function
that takes :KEY and :TEST parameters, and although I was aware of the
"NIL is OK, and makes my life easier" behavior, now that I'm looking I
can't quite find it spelt out, and I thought that it was clarified in
an earlier discussion. If anyone finds that one, do post a reference!

Thanks all
//JT
From: Pascal J. Bourguignon
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <874oz5g8oc.fsf@galatea.local>
Joshua Taylor <···········@gmail.com> writes:
> CL-USER 20 > (trace identity)
> (IDENTITY)

Well in any case, CL:TRACE is not specified to work on CL functions,
only on user functions.


> CL-USER 21 > (find 1 '(1 2 3))
> 1
> 
> CL-USER 22 > (find 1 '(1 2 3) :key 'identity)
> ;; If the key were called on every element, we'd see three calls here
> 0 IDENTITY > ...
>   >> SYSTEM::X : 1
> 0 IDENTITY < ...
>   << VALUE-0 : 1
> 1

In another implementation you could get the same result for both calls.

-- 
__Pascal Bourguignon__
From: Joshua Taylor
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <748c33fb-8069-4206-8327-a95eaebbd597@x10g2000yqk.googlegroups.com>
On Feb 7, 5:54 pm, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> Joshua Taylor <···········@gmail.com> writes:
> > CL-USER 20 > (trace identity)
> > (IDENTITY)
>
> Well in any case, CL:TRACE is not specified to work on CL functions,
> only on user functions.
> > CL-USER 21 > (find 1 '(1 2 3))
> > 1
>
> > CL-USER 22 > (find 1 '(1 2 3) :key 'identity)
> > ;; If the key were called on every element, we'd see three calls here
> > 0 IDENTITY > ...
> >   >> SYSTEM::X : 1
> > 0 IDENTITY < ...
> >   << VALUE-0 : 1
> > 1
>
> In another implementation you could get the same result for both calls.
>
> --
> __Pascal Bourguignon__

Of course, now I had to go and check up on TRACE too. Is this (TRACE
not guaranteed to work on CL functions, but only user functions)
because "If a function to be traced has been open-coded (e.g., because
it was declared inline), a call to that function might not produce
trace output." [1] and an implementation could open code all of its
functions? But then it's not clear that TRACE has to work on user
functions either.

I agree that another implementation could very well produce the same
results in each case. I hadn't meant to suggest that the results
LispWorks produced were guaranteed (or prohibited) by the standard.
Any thoughts about whether an implementation is permitted to call the
key function on more elements that the test is applied to? (I could
imagine this happening, for instance, when a key and test are
provided, and keys for upcoming comparisons could be performed in
parallel with some comparisons.) (Related is that SORT explicitly
makes no guarantee about how many times the key will be called on an
element of a sequence.)

//JT

[1] http://www.lispworks.com/documentation/HyperSpec/Body/m_tracec.htm#trace
From: Pascal J. Bourguignon
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <87r629e12q.fsf@galatea.local>
Joshua Taylor <···········@gmail.com> writes:

> On Feb 7, 5:54�pm, ····@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> Joshua Taylor <···········@gmail.com> writes:
>> > CL-USER 20 > (trace identity)
>> > (IDENTITY)
>>
>> Well in any case, CL:TRACE is not specified to work on CL functions,
>> only on user functions.
>> > CL-USER 21 > (find 1 '(1 2 3))
>> > 1
>>
>> > CL-USER 22 > (find 1 '(1 2 3) :key 'identity)
>> > ;; If the key were called on every element, we'd see three calls here
>> > 0 IDENTITY > ...
>> > � >> SYSTEM::X : 1
>> > 0 IDENTITY < ...
>> > � << VALUE-0 : 1
>> > 1
>>
>> In another implementation you could get the same result for both calls.
>>
>> --
>> __Pascal Bourguignon__
>
> Of course, now I had to go and check up on TRACE too. Is this (TRACE
> not guaranteed to work on CL functions, but only user functions)
> because "If a function to be traced has been open-coded (e.g., because
> it was declared inline), a call to that function might not produce
> trace output." [1] and an implementation could open code all of its
> functions? But then it's not clear that TRACE has to work on user
> functions either.
>
> I agree that another implementation could very well produce the same
> results in each case. I hadn't meant to suggest that the results
> LispWorks produced were guaranteed (or prohibited) by the standard.
> Any thoughts about whether an implementation is permitted to call the
> key function on more elements that the test is applied to? 

Well if it's your own function, then there are rules for TEST:
http://www.lispworks.com/documentation/HyperSpec/Body/17_b.htm
unfortunately, no such thing for KEY: they may be called any number of
times.

And of course, if you consider a function provided by your
implementation, it may be called independently.  Eg. IDENTITY or EQUAL
could be called inside FIND unrelated to the KEY parameter.

-- 
__Pascal Bourguignon__
From: TomSW
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <50c00f00-0a5b-4d68-b73e-e0b77a02e32e@d32g2000yqe.googlegroups.com>
> The reason I ask is because I just happened to write such a function
> that takes :KEY and :TEST parameters, and although I was aware of the
> "NIL is OK, and makes my life easier" behavior, now that I'm looking I
> can't quite find it spelt out, and I thought that it was clarified in
> an earlier discussion. If anyone finds that one, do post a reference!

In such cases I opted for
(foo item &key (key #'identity))

... thinking that it was clearer for users of the function & that
maybe the compiler would get optimise out (funcall #'identity object)
into just object. I had forgotten the case of explicitly providing a
nil :key - I think I'd prefer to have used some kind of "scrub-key"
function to deal with this case so as to mimic the CL sequence
functions.

cheers,
Tom
From: Barry Margolin
Subject: Re: NIL as :key to ASSOC, INTERSECTION, POSITION, &c.
Date: 
Message-ID: <barmar-9A035E.12275708022009@mara100-84.onlink.net>
In article 
<····································@d32g2000yqe.googlegroups.com>,
 TomSW <·············@gmail.com> wrote:

> In such cases I opted for
> (foo item &key (key #'identity))
> 
> ... thinking that it was clearer for users of the function & that
> maybe the compiler would get optimise out (funcall #'identity object)
> into just object.

The problem is that the compiler can't perform this optimization, 
because #'identity isn't literally in the funcall expression.  It will 
be (funcall key object), and there's no way for the compiler to know 
when KEY will contain #'identity.  Theoretically, the compiler could 
optimize this into

(if (eq key #'identity) object
    (funcall key object))

and possibly even pull the conditional out of the loop, but that's 
expecting a bit much.  Should every (funcall x y) expression be 
optimized like this, just on the off chance that X will be #'identity?

-- 
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 ***