eieio 里 cl-defmethod的 specializer `eql` 怎么用的?

Support for (eql VAL) specializers. These match if the argument is ‘eql’ to VAL.

我的理解是,参数和VAL是一个对象的话,这个方法就会被使用。 但是实际上并没有(如下)。

这是跟specific的等级有关吗?我没找到讲这个的。

(defclass myclass ()
  ())
myclass

(setq myinstance (make-instance 'myclass))
#s(#s(eieio--class myclass nil nil [] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data ( ...)) nil nil [] [] #s(#1) (:custom-groups nil)))


(cl-defmethod mymethod ((instance myclass))
  (message "Normal method"))
mymethod

(mymethod myinstance)
"Normal method"

(cl-defmethod mymethod ((instance (eql myinstance)))
  (message "Overload method"))
mymethod

(mymethod myinstance)
"Normal method"
(cl-defgeneric humanize (n))

(cl-defmethod humanize ((n (eql 42)))
  '"forty two")

(humanize 42)
;; => "forty two"
1 个赞

eql 不能用于比較 structclass。即

(defstruct foo a)

(eql (make-foo) (make-foo)) ; => nil

要找資料只能从 Common Lisp 找,我推薦 Common Lisp Hyperspec。然而 eieio 提供的不是完整的 CLOS,emacs-cl 也不完整的 Common Lisp,建议不要全信。

defgeneric 不是必須的,写出來主要是起 API 文檔作用。

1 个赞
(cl-defmethod mymethod ((instance (eql myinstance)))
  (message "Overload method"))

我想达到的效果是这个方法只对这个对象起效,这样是不行的么?

因为如果是同一个对象的话,eql返回t

比較同个对象用 eq

好的谢谢,我打算用id来区分各个对象。这样应该就可以了。

eqlhead(CL 并没有实现 head?)一直有问题。

到 Emacs 28 才修复了 eql,像 CL 一样支持 (eql sym) ,可见并没有多少人使用。

Emacs 27-

(with-emacs-27.1
 (require 'cl-generic)
 (cl-defgeneric test (x)               (print "not implemented"))
 (cl-defmethod test  ((x (eql 42)))    (print "eql 42"))
 (cl-defmethod test  ((x (eql 'foo)))  (print "eql foo"))
 (test 42)
 (test 'foo))
;; => "eql 42"
;;    "not implemented"

Emacs 28+

(with-emacs-nightly
 (require 'cl-generic)
 (cl-defgeneric test (x)               (print "not implemented"))
 (cl-defmethod test  ((x (eql 42)))    (print "eql 42"))
 (cl-defmethod test  ((x (eql 'foo)))  (print "eql foo"))
 (test 42)
 (test 'foo))
;; => "eql 42"
;;    "eql foo"

Common Lisp

#+BEGIN_SRC lisp :results output :playonline '(rextester "lisp")
(defmethod test  ((x (eql 42)))    (print "eql 42"))
(defmethod test  ((x (eql 'foo)))  (print "eql foo"))
(test 42)
(test 'foo)
#+END_SRC

#+RESULTS:
: 
: "eql 42" 
: "eql foo" 

Emacs 28 虽然修复了 eql,但 head 依然不支持 (head sym),这不一致看着真难受。