Reputation: 467
Can I mimic different constructors in CL?
To elaborate -- in, say, C++, I can make different constructors for the same class depending on what arguments are passed.
Can I do this with CLOS? Probably having different initialize-instance
s key args or something like that?
Upvotes: 0
Views: 511
Reputation:
One approach to doing this is to have a secondary initialization method:
(defclass myclass ()
((s1 :initarg :s1 :accessor s1)))
(defgeneric initialize-myclass (dispatch class &key))
(defmethod initialize-instance :after ((c myclass) &rest args &key (dispatch 'normal)
&allow-other-keys)
(apply #'initialize-myclass dispatch c args))
(defmethod initialize-myclass ((dispatch (eql 'normal)) (class myclass) &key))
(defmethod initialize-myclass ((dispatch (eql 'special)) (class myclass)
&key x &allow-other-keys)
(print x))
Now you can say
(make-instance 'myclass :dispatch 'special ...)
For instance. Note this is not necessarily a good way of doing it, but it does work, and I've used it. Note also I may have got the keyword-argument defaulting wrong: I never remember where you need to say &allow-other-keys
& where you don't, and where the right place to say it is.
The basic problem here is that we want an additional thing to dispatch on: initialize-instance
can dispatch on the class of the object being defined, but that's all it can dispatch on. In particular it can't dispatch on one of its keyword arguments because you can't do that in CLOS. But we can take one of its keyword arguments (dispatch
here) and 'bless' it as a positional argument to a secondary initialization generic function, which then can dispatch on that argument.
Upvotes: 1
Reputation: 1576
Well, initialize-instance
is typically defined as an :after
method to run some sort of post-processing once the instance has been initialized through make-instance
. What you might do is use parametric polymorphism (dispatching on parameters) and have different methods initialize your instance based on the parameters supplied. Consider the following example:
CL-USER> (defclass my-class ()
((a :initarg :a
:accessor my-class-a)))
#<STANDARD-CLASS COMMON-LISP-USER::MY-CLASS>
CL-USER> (defmethod make-my-class ((a number))
(make-instance 'my-class :a (format nil "Look ma, a number ~a" a)))
#<STANDARD-METHOD COMMON-LISP-USER::MAKE-MY-CLASS (NUMBER) {1016445273}>
CL-USER> (defmethod make-my-class ((a string))
(make-instance 'my-class :a (format nil "Look ma, a string ~a" a)))
#<STANDARD-METHOD COMMON-LISP-USER::MAKE-MY-CLASS (STRING) {10166065C3}>
CL-USER> (make-my-class 10)
#<MY-CLASS {1016690E33}>
CL-USER> (my-class-a *)
"Look ma, a number 10"
CL-USER> (make-my-class "foo")
#<MY-CLASS {1016694CD3}>
CL-USER> (my-class-a *)
"Look ma, a string foo"
As you can see, the method make-my-class
dispatches on its argument and initializes my-class
accordingly.
Upvotes: 0