如何将 CLOS 用于类型而不是实例?

How to use CLOS for types rather than instances?

想象一些基 class pgj-model 上面定义了很多方法,但没有插槽。现在考虑:

(defclass cat (pgj-model) ())

(let ((cat (make-instance 'cat)))
  (ensure-backend cat)
  (insert cat (obj "name" "Joey" "coat" "tabby")))

如果我们想自定义新的 class cat 我们可以像这样专门化一个方法:

(defmethod insert ((model cat) (object hash-table))
  ;; Do something interesting
)

这一切都很好。但是 pgj-modelcat 都没有任何槽,它们是无状态的。这是设计使然,因为我只对它们感兴趣,因为它们是方法可以专门用于的 lisp 类型。所以似乎 annoying/confusing 在任何你想调用此类方法的地方创建 class cat 的实例。

一个想法是:

(defparameter *cat* (make-instance 'cat))   ; There can be only one...
...
(insert *cat* (obj "name" "Joey" "coat" "tabby"))

另一种方法是在我的所有通用函数上专门化一个附加方法,如下所示:

(defmethod insert ((model symbol) object)
  (insert (make-instance model) object))

(insert 'cat (obj "name" "Joey" "coat" "tabby"))

这看起来不错,但 1) 可能会使用户感到困惑,2) 使用样板文件使通用函数膨胀,以及 3) 为每个方法调用增加一些开销。

其他建议?

您可以使用 eql 专业化程序来调度符号标识而不是 class:

(defgeneric insert (thing container))

(defmethod insert ((thing (eql 'cat)) (container hash-table))
  ...)

一种可能的方法是创建自己的单例元class,但我不建议这样做,因为这不是好的做法,这样您就可以将 allocate-instance 专门化为 call-next-method第一次并将新实例缓存到 return 每隔一次。

(defclass singleton-class (standard-class)
  ((instance :initform nil :accessor singleton-class-instance)))

(defmethod allocate-instance ((class singleton-class) &rest initargs)
  (declare (ignore initargs))
  (with-slots (instance) class
    (or instance
        (setf instance (call-next-method)))))

(defclass pgj-model ()
  ()
  (:metaclass singleton-class))

(defclass cat (pgj-model)
  ()
  (:metaclass singleton-class))

请注意,您需要为每个 class 声明元class,它不是继承的。

您也可以以类似的方式专门化 make-instance,这样它在第一次后就不会遵循通常的初始化过程。

我没有处理同步,因为你的对象是无状态的,但如果你需要可靠地保持单一身份,你可能需要解决它。

最后,这绝对不是一个好的做法,因为它颠覆了 allocate-instance 的目的,因此也颠覆了 make-instance 的目的。根据规范,Lisp 实现可以只编译以下函数:

(defun test-singleton-class (class)
  (eq (make-instance class)
      (make-instance class)))

通过优化它,就好像它是这样定义的:

(defun test-singleton-class (class)
  ;; possibly inlined make-instance calls
  nil)

此代码不符合预期。

所以,我的实际建议是继续使用全局变量,或者更确切地说是全局 reader 函数,例如不可设置的 (cat).