Книга: Practical Common Lisp
Mixing Different Parameter Types
Mixing Different Parameter Types
It's possible, but rare, to use all four flavors of parameters in a single function. Whenever more than one flavor of parameter is used, they must be declared in the order I've discussed them: first the names of the required parameters, then the optional parameters, then the rest parameter, and finally the keyword parameters. Typically, however, in functions that use multiple flavors of parameters, you'll combine required parameters with one other flavor or possibly combine &optional
and &rest
parameters. The other two combinations, either &optional
or &rest
parameters combined with &key
parameters, can lead to somewhat surprising behavior.
Combining &optional
and &key
parameters yields surprising enough results that you should probably avoid it altogether. The problem is that if a caller doesn't supply values for all the optional parameters, then those parameters will eat up the keywords and values intended for the keyword parameters. For instance, this function unwisely mixes &optional
and &key
parameters:
(defun foo (x &optional y &key z) (list x y z))
If called like this, it works fine:
(foo 1 2 :z 3) ==> (1 2 3)
And this is also fine:
(foo 1) ==> (1 nil nil)
But this will signal an error:
(foo 1 :z 3) ==> ERROR
This is because the keyword :z
is taken as a value to fill the optional y
parameter, leaving only the argument 3 to be processed. At that point, Lisp will be expecting either a keyword/value pair or nothing and will complain. Perhaps even worse, if the function had had two &optional
parameters, this last call would have resulted in the values :z
and 3 being bound to the two &optional
parameters and the &key
parameter z
getting the default value NIL
with no indication that anything was amiss.
In general, if you find yourself writing a function that uses both &optional
and &key
parameters, you should probably just change it to use all &key
parameters—they're more flexible, and you can always add new keyword parameters without disturbing existing callers of the function. You can also remove keyword parameters, as long as no one is using them.[60] In general, using keyword parameters helps make code much easier to maintain and evolve—if you need to add some new behavior to a function that requires new parameters, you can add keyword parameters without having to touch, or even recompile, any existing code that calls the function.
You can safely combine &rest
and &key
parameters, but the behavior may be a bit surprising initially. Normally the presence of either &rest
or &key
in a parameter list causes all the values remaining after the required and &optional
parameters have been filled in to be processed in a particular way—either gathered into a list for a &rest
parameter or assigned to the appropriate &key
parameters based on the keywords. If both &rest
and &key
appear in a parameter list, then both things happen—all the remaining values, which include the keywords themselves, are gathered into a list that's bound to the &rest
parameter, and the appropriate values are also bound to the &key
parameters. So, given this function:
(defun foo (&rest rest &key a b c) (list rest a b c))
you get this result:
(foo :a 1 :b 2 :c 3) ==> ((:A 1 :B 2 :C 3) 1 2 3)
- Types of Subprojects
- Appendix C. ICMP types
- Parameter problem
- Displacement of rules to different chains
- It’s All The Same, Just Different
- Perl Variable Types
- Printing Out the Parameters
- Positional Parameters
- Types of Kernels
- Chapter 4. The Linux KernelA Different Perspective
- 8.2.2. Module Parameters
- 3.1.7 Variable Types