Search code examples
classlispcommon-lisp

Different initialization, Common Lisp


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-instances key args or something like that?


Solution

  • 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.