我最近在reddit上问过这个问题,但我想也许我会在这里得到更好的答案。
我编写的宏通常采用一个符号或符号列表,有时这些符号表示变量或函数。在这些情况下,我很想编写宏,以便可以对它们的参数进行引用或共享。这是一个具体的例子:
在emacs lisp中,功能add-hook将HOOK-FN添加到名为HOOK的列表中。
(add-hook 'hook #'hook-fn)
通常,您想将多个项目添加到HOOK或将HOOK-FN添加到多个钩子,但是add-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)会触发我的自动补全,这有助于我更快地键入该函数或符号。
但是,我给人的印象是这不是轻率的惯例。为什么?是否有充分的理由说明为什么您不应该在宏中使用带引号的参数?
您当然可以这样做,但是它看起来有些奇怪,因为它混合了视觉信号,分别表示“功能正常,正常评估”和“宏功能,特殊评估”。
为什么不像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)))
Etvoilà:
(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)