[pro] why :key arguments?

Stas Boukarev stassats at gmail.com
Mon Jul 4 11:46:07 UTC 2011


Tamas Papp <tkpapp at gmail.com> writes:

> On Mon, 04 Jul 2011 14:20:32 +0400, Stas Boukarev wrote:
>
>> Tamas Papp <tkpapp at gmail.com> writes:
>> 
>>> On Mon, 04 Jul 2011 11:39:39 +0200, Hans Hübner wrote:
>>>
>>>> On Mon, Jul 4, 2011 at 11:31 AM, Tamas Papp <tkpapp at gmail.com> wrote:
>>>>> Why do some CL library functions have :key arguments?
>>>> [...]
>>>>> but it is a bit cumbersome.  I can make my code simpler by relying on
>>>>> calls like
>>>>>
>>>>> (quantiles (map 'vector key vector) quantiles)
>>>> 
>>>> This not only conses "a bit more", it also duplicates traversal
>>>> efforts - The original list must be traversed, and the consed-up list
>>>> of key values as well.  I think it is prudent that the CL library
>>>> functions offer ways to reduce consing for cases where "a bit" is too
>>>> much (and "a bit" can become a lot if a program operates on long
>>>> lists).
>>>
>>> I understand this.  My main question is: why not do this with compiler
>>> macros?  Is there any reason for this, other than historical?
>> Because it's not easy to do with compiler macros.
>
> Can you (or someone) please elaborate on that?  I have just started 
> reading up on compiler macros, and I don't understand why.
>
Suppose you have a function SUM,

(defun sum (list)
  (loop for i in list
        sum i))

Now if you want to optimize (sum (map 'list KEY list)), you would
need to write an additional function.

(defun sum-key (list key)
  (loop for i in list
        sum (funcall key i)))

And then you would need to write a compiler macro, which would match
(map 'list KEY sequence) and transform it into (sum-key sequence KEY),
which is not that hard, if that's only what you have. Now what if you
want it to work with (mapcar KEY list) too. It becomes more and more
cumbersome as it gets more general.

And now you need two functions, a clever compiler macro (perhaps using
some pattern matching library), and which may not do what the user
expects.

While you could have all that with just one function:

(defun sum (list &key key)
  (loop for i in list
        sum (if key
                (funcall key i)
                i)))
Other disadvantages of compiler-macros:

* They are not guaranteed to be expanded. Some implementations may
  ignore them.
* They can't be used with APPLY or FUNCALL.
* You can't compose them
  e.g. if you wanted to write
  (defun two-sum (list1 list2)
     (+ (sum list1) (sum list2)))
  You would need to write a second compiler macro. While with KEY
  keyword you could just pass it along.

-- 
With best regards, Stas.




More information about the pro mailing list