There is no standard way to do this in CL. You could make the function take everything in the rest
parameter, and parse the keyword arguments yourself, if you don't want to design the API differently.
That said, here are a few points for further exploration. They might be useful in specific use cases, but are rather limited and exploit implementation-dependent behavior.
You can use &allow-other-keys
in the lambda list or :allow-other-keys t
when calling foo
to prevent the unknown key error, but rest
will also include the keys and values of your keyword arguments:
CL-USER> (defun foo (&rest rest &key (a 1) (b 2))
(list rest a b))
FOO
CL-USER> (foo)
(NIL 1 2)
CL-USER> (foo :a 100 12 3 :allow-other-keys t)
((:A 100 12 3 :ALLOW-OTHER-KEYS T) 100 2)
CL-USER> (defun foo (&rest rest &key (a 1) (b 2) &allow-other-keys)
(list rest a b))
FOO
CL-USER> (foo)
(NIL 1 2)
CL-USER> (foo :a 100 12 3)
((:A 100 12 3) 100 2)
As acelent correctly points out in a comment below, this might signal an error. It works for me in CLISP, SBCL, and CCL under the default optimize settings, but by the standard, keyword argument names (i.e. the first of each pair of arguments) must be symbols. Whether or not this works depends on the safety level and is implementation-dependent. It should signal an error (on conforming implementations) in safe code (safety level of 3).
In general, allowing other keys can be useful for passing keyword arguments through, but is not exactly what you wanted. One quick and dirty way might be filtering for keyword parameters in rest
and just dropping them and their succeeding elements. Something like this:
CL-USER> (defun foo (&rest rest &key (a 1) (b 2) &allow-other-keys)
(let ((rest (loop for (key value) on rest by #'cddr
unless (keywordp key)
append (list key value))))
(list rest a b)))
FOO
CL-USER> (foo)
(NIL 1 2)
CL-USER> (foo :a 100 12 3)
((12 3) 100 2)
Which will, alas, by the standard only work for even numbers of arguments, as coredump points out in his answer. (It might work with an odd number of arguments under some implementations for some safety levels, but it didn't work in the implementations I tested.) Also, obviously it isn't robust in other ways (different positions of keyword arguments etc.), and not meant for production use, but just as a starting point for possible exploration.
The proper solution would involve writing your own keyword argument parser, and use it with rest
, or, as pointed out in coredump's answer, using a different API. Another point worth mentioning is that in CL, apply
ing large numbers of arguments is generally not a good idea since it might lead to inefficient code. What's worse, it is also not very reliable, since the number of allowed arguments is implementation-dependent. For example, under CCL on my system, call-arguments-limit
is 65536. It may be significantly – even orders of magnitude – smaller under other implementations and systems. So, in general, prefer reduce
ing to apply
ing large numbers of arguments.
append (list key value)
should probably benconc (list key value)
; you're generating the new list structure, so there's no need to copy it so many times. Alternatively, you could alsocollect key collect value
, I suppose. – Slut