【问题标题】:Can't call functions defined in macro with names generated by make-symbol无法使用 make-symbol 生成的名称调用宏中定义的函数
【发布时间】:2015-10-23 04:04:02
【问题描述】:

我正在尝试编写一个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 中呢?

【问题讨论】:

    标签: emacs macros lisp elisp


    【解决方案1】:

    您的代码也不能在 CL 中运行。

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

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

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

    当您评估 (foo-1) 时,Emacs Lisp 阅读器会尝试查找 interned 符号 foo-1 的函数绑定,而不是您的宏创建的新的非interned 符号。

    您需要改用intern:它使符号“普遍可用”,可以这么说:

    (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

    【讨论】:

    • 确实如此。自从我编写任何 CL/Clojure/Elisp 样式的宏已经有一段时间了,这是一个容易犯的错误,显然在 Elisp 中比在 CL 中更容易:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    • 1970-01-01
    • 2016-08-02
    • 2011-08-18
    • 2015-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多