无法使用 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-1
和 foo-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"
备注:
- 如果您使用的是 CL,未保留的符号将打印为
#:foo-1
,您的问题的根源对您来说是显而易见的。
- 非常您真的需要使用
make-symbol
。通常,您想要使用 intern
或 gensym
.
我正在尝试编写一个 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-1
和 foo-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"
备注:
- 如果您使用的是 CL,未保留的符号将打印为
#:foo-1
,您的问题的根源对您来说是显而易见的。 - 非常您真的需要使用
make-symbol
。通常,您想要使用intern
或gensym
.