Time and again I find myself in the situation that a function A needs to call function B with or without keyword arguments, depending on whether such keyword arguments have been given to function A.
Maybe a silly MWE is easier to follow: let's ask a boy called Tommy how his day at school was:
(defun answer (&key (math "boring" math-tested?)
(physics "heavy" physics-tested?))
(if math-tested?
(if physics-tested?
(format t "I got an ~A from math and a ~A from physics!" math physics)
(format t "I got an ~A from math." math))
(if physics-tested?
(format t "I got an ~A from physics." physics)
(format t "Math was ~A and physics was ~A." math physics))))
(defun father-son-talk (&key (math nil math-tested?)
(physics nil physics-tested?))
(format t "Hello Tommy, how was school today?~%")
(cond ((and math-tested? physics-tested?)
(answer :math math :physics physics))
(math-tested?
(answer :math math))
(physics-tested?
(answer :physics physics))
(t (answer))))
This works as intended but it is irksome for several reasons:
The calls to
answer
are basically repeated. Ifanswer
had several normal arguments in addition to the keyword ones, I have to be extra careful to do the same in all four cases. Worse yet when maintaining such a thing.The complexity grows exponentially with the number of keyword arguments. What if the father would ask about English, geology and Tommy's lunch as well?
Further confusion can easily arise if the father and son have different default arguments (one might be tempted to write
(answer :math math :physics physics)
).
Question: Assuming that answer
is a part of the interface I have to conform to, how can I simplify father-son-talk
? Ideally, I would like something like
(defun father-son-talk (&key (math nil math-tested?)
(physics nil physics-tested?))
(format t "Hello Tommy, how was school today?~%")
(answer :math (math math-tested?) :physics (physics physics-tested?)))