Kaz Kylheku
10/14/2015 6:45:00 PM
On 2015-10-14, Jim Newton <jimka.issy@gmail.com> wrote:
> I'd like to be able to define default values on the optional argument
> declarations in the generic function. Then if any method does not provide an
> default value for its optional argument, the value should come from the
> generic function. Here's an example.
>
> (defgeneric F (x &key (x 100) (y 200)))
>
> (defmethod F ((x A) &key (x 1) (y 2))
> ...)
>
> (defmethod F ((x B) &key (x nil) y) ; x defaults to nil, but y defaults to 200 as per the defgeneric
> ...)
>
> (defmethod F ((x C) &key x y) ; x and y both default from the defgeneric, 100,100 respectively
> ...)
>
> Of course it is normally easy to workaround this, such as declare global
> variables storing the defaults, and explicitly provide that global (or
> lexical) variable as the default value in each method definition.
That workaround is not satisfactory.
You should think about how these x and y quantities are related to the
classes.
Perhaps they need to be :allocation :class slots!
Int the code below, we have an ad-nauseum repeititon of the same &key
part of the lambda list in all the methods.
The default values are then controlled via slots in the classes.
Inheritance lets us override the defaults in a base class with
different values in the derived class.
The class framework has two important key parameters, "pparam" and "qparam"
which are used in the "foo" method. The foo method obtains the pparam
value based on the type of the left object, and the qparam based on the
right object.
(defclass base ()
((pparam :initform 42 :allocation :class :accessor pparam)
(qparam :initform 's :allocation :class :accessor qparam)))
(defclass derived (base)
((pparam :initform 43 :allocation :class :accessor pparam)
(qparam :initform 't :allocation :class :accessor qparam)))
(defgeneric foo (obj1 obj2 &key p q))
(defmethod foo ((obj1 base) (obj2 base) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))
(defmethod foo ((obj1 base) (obj2 derived) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))
(defmethod foo ((obj1 derived) (obj2 base) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))
(defmethod foo ((obj1 derived) (obj2 derived) &key (p (pparam obj1)) (q (qparam obj2)))
(format t "p: ~s, q: ~s~%" p q))
REPL:
P from base, Q from derived:
[1]> (foo (make-instance 'base) (make-instance 'derived))
p: 42, q: T
NIL
Vice versa:
[2]> (foo (make-instance 'derived) (make-instance 'base))
p: 43, q: S
NIL
Both overriden via keyword arguments:
[3]> (foo (make-instance 'derived) (make-instance 'base) :p 'over :q 'ride)
p: OVER, q: RIDE
NIL
What is missing?
- The repeated &key list is cumbersome. Solution: perhaps tuck it behind a macro?
- Preparing the defaults via inheritance is cumbersome. Solution: macro. :)