Mixing optional and keyword arguments
Mixing optional and keyword arguments is still something that's not all that easy to do. If a function accepts an optional argument, then you won't be able to use the keyword arguments unless the optional argument is also provided. Otherwise, the first keyword would be interpreted as the optional argument, and so on. See, for instance, this Stack Overflow question: How can I have optional arguments AND keyword arguments to the same function?. As the answer to that question points out, it's usually a bug-prone practice to mix optional and keyword arguments. Common Lisp does it with read-from-string
, and it often leads people into trouble.
What you're proposing, though, isn't just having a function that uses both keyword and optional arguments, but, from the sounds of it, is actually doing some checking of the types of arguments, and taking one behavior in one case, and another in another. In this case, if i
is supposed to be a number, then you could check the first argument, and if it's a number, then treat it as the optional argument, and the rest as keyword arguments, and if it's not a number, then treat the whole list as keyword arguments. You can do that with an &rest
argument that you destructure in different ways:
(defun frob (&rest args)
(flet ((frob-driver (i size)
(list i size)))
(if (or (endp args) (numberp (first args)))
;; no args, or the first argument is a number (and thus
;; not a keyword argument)...
(destructuring-bind (&optional (i 'default-i) &key (size 'default-size)) args
(frob-driver i size))
;; otherwise, there are some non-numeric arguments at
;; beginning, so it must be the keyword list, and that the
;; "optional" wasn't provided.
(destructuring-bind (&key (size 'default-size) &aux (i 'default-i)) args
(frob-driver i size)))))
(frob 10 :size 50) ; give i and size
;=> (10 50)
(frob :size 60) ; give size, but not i
;=> (default-i 60)
(frob 40) ; give i, but not size
;=> (40 default-size)
(frob) ; give neither
;=> (default-i default-size)
Keyword arguments without keyword symbols
In the comments, you mentioned that you'd like to be able to use non-keyword symbols as keywords in argument lists. This is easy enough. In the HyperSpec, §3.4.1 Ordinary Lambda Lists describes the syntax for keyword arguments:
[&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
This means that you can define functions like this:
(defun frob (&key foo ((bar-keyword bar-variable) 'default-baz))
(list foo bar-variable))
(frob :foo 1 'bar-keyword 2)
;=> (1 2)
(frob :foo 3)
;=> (3 default-baz)
(frob 'bar-keyword 2)
;=> (nil 2)
(test :size)
? When you providearg
andi
, then:size
has to be used with a value, e.g.,(test arg i :size size)
. I'd understand wanting to use the keyword arguments without providing all the optional arguments, e.g.,(test :size size)
,(test arg :size size)
, but I'm not sure what you'd have(test :size)
. Implementing what you want won't be all that difficult, but you'll need to specify it more concretely first. – Superfetation(defun test (arg i &key size) ...)
with the documentation: "arg—either a list or the symbol :size. If arg is a list, then if the empty list …, else if a non-empty list …. If arg is the symbol :size, then … and the other arguments are ignored." – Superfetation