有对为 generalized variable 定义 setter method 了解的小伙伴吗?

gv-define-setter 只能用来定义基础的 setter 函数,类似于 car/setcar, aref/aset 之类的。如果 name 对应的 getter 函数接受了 gv 参数它是不能正常工作的。

具体原因可见源代码:gv-define-setter 内部调用了 gv--defsettergv--defsetter 展开过程中会使用 macroexp-let2 来保证 setter 函数将要应用到的参数们只被求值一次。

(defun gv--defsetter (name setter do args &optional vars)
 "Helper function used by code generated by `gv-define-setter'.
NAME is the name of the getter function.
SETTER is a function that generates the code for the setter.
NAME accept ARGS as arguments and SETTER accepts (NEWVAL . ARGS).
VARS is used internally for recursive calls."
 (if (null args)
     (let ((vars (nreverse vars)))
       (funcall do `(,name ,@vars) (lambda (v) (apply setter v vars))))
   ;; FIXME: Often it would be OK to skip this `let', but in general,
   ;; `do' may have all kinds of side-effects.
   (macroexp-let2 nil v (car args)
     (gv--defsetter name setter do (cdr args) (cons v vars)))))

这样,参数表里面的 gv 参数的值就直接保存到了局部变量中,就不知道它对应的 setter 函数相关信息了。(你的代码中的 gv 变量就是 integer,它的 setter form 是 (setq integer …),但是这个没法得到。

上面的解决方案中使用了 gv-letplacegv 参数中获取到了 gettersetter 表达式,从而能够正常工作。

1 个赞