无法使用 make-symbol 生成的名称调用宏中定义的函数

Can't call functions defined in macro with names generated by make-symbol

我正在尝试编写一个 ELisp 宏来根据一些通用数据生成多个函数。例如,当我想计算 fn 名称时,我写了类似的东西(我暂时忽略卫生,我将符号文字传递到宏中,因此评估无关紧要):

(cl-defmacro def-fns (sym)
  "SYM."
  (let ((s1 (make-symbol (concat (symbol-name sym) "-1")))
        (s2 (make-symbol (concat (symbol-name sym) "-2"))))
    `(progn (defun ,s1 () (+ 1 2 3))
            (defun ,s2 () "six"))))

我希望在调用时生成 2 个 fns,称为 foo-1foo-2

然后我应该能够像这样调用宏和 fns:

(def-fns foo)
(foo-1)
;; => 6
(foo-2)
;; -> "six

甚至 Emacs 中 (def-fns foo) 的宏展开也表明情况应该如此:

(progn
  (defun foo-1 nil (+ 1 2 3))
  (defun foo-2 nil "six"))

但是,当我计算 def-fns 定义并调用它时,它 不会 生成那些函数。为什么会这样?此技术适用于 Common Lisp 和 Clojure(它们具有非常相似的宏系统),那么为什么不适用于 ELisp?

您的代码在 CL 中也不起作用。

问题出在 make-symbol - 它创建了一个 new 符号,因此

(eq (make-symbol "A") (make-symbol "A"))
==> nil

这意味着您的宏创建了函数,但将它们绑定到您不再拥有句柄的符号。

当您计算 (foo-1) 时,Emacs Lisp reader 试图找到 interned 符号 foo-1 的函数绑定,而不是新的您的宏创建的非内部符号。

您需要改用 intern:它使符号 "generally available",可以这么说:

(eq (intern "a") (intern "a))
==> t

因此,更正后的代码如下所示:

(defmacro def-fns (sym)
  "SYM."
  (let ((s1 (intern (concat (symbol-name sym) "-1")))
        (s2 (intern (concat (symbol-name sym) "-2"))))
    `(progn (defun ,s1 () (+ 1 2 3))
            (defun ,s2 () "six"))))
(def-fns foo)
(foo-1)
==> 6
(foo-2)
==> "six"

备注:

  1. 如果您使用的是 CL,未保留的符号将打印为 #:foo-1,您的问题的根源对您来说是显而易见的。
  2. 非常真的需要使用make-symbol。通常,您想要使用 interngensym.