How can i specialize a generic function to take symbols designating subclasses of given class.
For example:
(defclass a () ())
(defclass b (a) ())
(defclass c (b) ())
(defclass d () ())
(defgeneric fun (param))
(defmethod fun ((param (<subclass of> a)))
(format t "~a is a subclass of A~%" param))
(fun 'c) ;-> "C is a subclass of A"
(fun 'd) ;-> Error: not found method for generic function call (fun 'd)
Is such dispatching possible with CLOS? And if it is, what should I write instead of “subclass of“?
You won’t be able to easily perform this exact task using only CLOS dispatching.
Before I continue, I think some brief notes on terminology is important.
The Common Lisp HyperSpec glossary defines “subclass” in this way:
This definition, while intuitive, seems odd to me as I’d expect that to be the definition of a “proper subclass”. However, all classes are types, and it defines “subtype” as:
Note the parenthetical: “Every type is a subtype of itself.”
It also defines a “proper subtype”:
So, in your example, B and C are subclasses of A, and also subtypes. On the other hand B, C, and A are subtypes of A.
The thing one puts in defmethod is a “parameter specializer name”. It can be a symbol, a class (which is a little hard to type), or a list starting with eql. If you provide a symbol, it specifies the class named by that symbol (which is, of course, a type). An eql list specifies a type consisting of objects which are eql to the thing in the list.
The method will match any object which is a member of the type the specializer specifies. And of course, a member of a subtype of X is also a member of X.
So your first problem is that you are passing symbol objects to your method; every symbol is of type
SYMBOL. A symbol that happens to name a class is no different in this respect; it’s only relationship to the class is that it is the class’s name, which is not a subtype relation.There are class objects (returned by
find-class), but they’re no better than symbols for method specialization here because the type of a class object is usually the same as the type of its subclasses’ class objects.So, you’re left using instances or reading AMOP to learn how to create your own types of generic functions.
Once you have an instance, you can write the method like this:
If you have an easy way to retrieve instances of your classes, you could write this wrapper:
Then you’ll be able to pass symbols to fun and get the results you want.
If you want to use AMOP functions (which were not specified by the standard but are widely available, see Closer Project), you can define
retrieve-instancelike this:Note that method dispatch is just about the only thing the result of
class-prototypeis good for; don’t try to modify it or anything like that.