Here is another example which leads to more questions.
This defines a class called HAS-CALL. If a class inherits from HAS-CALL, then funcall-ing an object of that class causes the CALL generic function to be called with the object as first argument, and any remaining arguments following. Thus such a class can define a method CALL and that will implement be funcall-able behaviour.
This works great, except that the class of the object must be defined with
(:metaclass funcallable-standard-class)
in its defclass form.
There must be a way to make this automatic. I.e., to somehow create a subclass of FUNCALLABLE-STANDARD-CLASS, and use that derived class as the metaclass of HAS-CALL, with the result being that classes which inherit from HAS-CALL will also inherit the metaclass.
Does someone have a recipe for that?
---------------------------------
(defpackage "MY-APP"
(:use :cl :closer-mop)
(:shadowing-import-from :closer-mop standard-generic-function defmethod defgeneric))
(in-package "MY-APP")
(setf (find-class 'has-call) nil)
(setf (find-class 'X) nil)
(setf (find-class 'W) nil)
(setf (find-class 'U) nil)
(defgeneric call (obj &rest arguments))
(defclass has-call (standard-object function) ;; must list standard-object before function, else there is no applicable method for initialize-instance
()
(:metaclass funcallable-standard-class))
(defmethod initialize-instance :after ((self has-call) &key)
(set-funcallable-instance-function
self
#'(lambda (&rest arguments)
(apply #'call self arguments))))
(defmethod call ((self has-call) &rest arguments)
(declare (ignore arguments))
(cerror "Return nil" "class ~A does not define a CALL method" (class-name (class-of self)))
nil)
(defclass X (has-call)
()
(:metaclass funcallable-standard-class))
(defmethod call ((self X) &rest arguments)
(cons 1 (list self arguments)))
(funcall (make-instance 'X) 1 2 3)
(defclass W (X)
()
(:metaclass funcallable-standard-class))
(defmethod call ((self W) &rest arguments)
(cons 2 (list self arguments)))
(funcall (make-instance 'W) 1 2 3)
(defclass U (W)
()
(:metaclass funcallable-standard-class))
(funcall (make-instance 'U) 1 2 3)