为什么不允许在宏中引用参数

Why not allow quoted arguments in macros

我最近在 reddit 上问过这个问题,但我想也许我会在这里得到更好的答案。

我编写的宏通常采用符号或符号列表,有时这些符号代表变量或函数。在这些情况下,我很想编写宏,以便可以引用或引用它们的参数。这是一个具体的例子:

在 emacs lisp 中,add-hook 函数将 HOOK-FN 添加到名为 HOOK 的列表中。

(add-hook 'hook #'hook-fn)

经常你想添加几个项目到HOOK或者你想HOOK-FN到几个钩子,但是add-hook一次只接受一个钩子或一个hook-fn。所以,我为这个叫做add-hook做了一个宏!可以接受多个参数。

(add-hook! hook-name (fn1 fn2 fn3))

;; or

(add-hook! (hook-name1 hook-name2) hook-fn)

我倾向于允许引用或尖锐引用宏的参数。就像他们在函数调用中一样。

(add-hook! 'hook-name #'fn)

为此,我将使用这样的函数来去除每个参数的引号。

(defun unquote (form)
  "Unquote a form if it is quoted, otherwise return form."
  (if (member (car-safe it) '(quote function))
      (cadr form)
    form))

我愿意这样做是因为引用和尖锐引用 (1) 清楚地表明我指的是函数还是符号,并且 (2) 触发了我的自动完成功能,这有助于我更快地键入函数或符号。

但是,我的印象是这不是 lisp 中的惯例。为什么?有充分的理由说明您不应在宏中使用引号吗?

你当然可以这样做,但它看起来有点奇怪,因为它混合了“这里是函数,正常评估”和“这里是宏,特殊评估”的视觉信号。

为什么不让 add-hook-fns 成为一个(非常简单的)函数,就像 add-hook 一样?那么所有的引用自然就到位了,甚至是必需的。

(defun add-hook-fns (hook fns)
  (dolist (fn fns)
    (add-hook hook fn)))

这将看起来像这样的调用:

(add-hook-fns 'some-hook (list #'foo #'bar #'baz))

如果您不想在其中显式调用 list,您可以使用宏来消除它:

(defmacro add-hook-fns* (hook fns)
  `(add-hook-fns ,hook (list ,@fns)))

瞧瞧:

(add-hook-fns* 'some-hook (#'foo #'bar #'baz))

但是,我不建议这样做,请在开头查看原因。

相反,我仍然只使用一个函数,但带有其余参数:

(defun add-hook-fns (hook &rest fns)
  (dolist (fn fns)
    (add-hook hook fn)))

现在:

(add-hook-fns 'some-hook #'foo #'bar #'baz)