Common Lisp 宏扩展作为 rest 传递的参数列表,如:(foo (expand bar 100 200 300)) => (foo (bar 100) (bar 200) (bar 300))
Common Lisp Macro to expand an argument list passed as rest like: (foo (expand bar 100 200 300)) => (foo (bar 100) (bar 200) (bar 300))
抱歉标题笨拙,但我很难用几句话描述我要找的东西...
我正在做一个 Common Lisp DSL 项目,我想知道以下是否可行:
DSL 可能有几个功能
(defun foo (&rest rest))
和
(defun bar (arg))
将按以下方式使用:
(foo (bar 100) (bar 200) (bar 300) (bar 400) (bar 500))
等
现在有很多多余的输入,所以我想知道是否可以创建一个扩展宏
这将允许
(foo (expand bar 100 200 300 400 500))
不改变 foo
本身?
不需要宏:
(defun foo (&rest rest)
(apply #'+ rest)) ; for example add all together
(defun bar (arg)
(1+ arg)) ; 1+ is only an example here
(apply #'foo (mapcar #'bar '(100 200 300 400 500)))
不,如果不更改您正在定义的函数的签名(或者可能使用一些重新定义宏扩展的毛茸茸的东西),这是无法完成的:您有我称之为 'spread/nospread impedance mismatch' 的东西,它不能用 CL 中的标准宏解决。
'nospread' 函数是将所有参数包装成一个形式的函数。 'spread' 函数的每个参数都有一个形式。我在使用 InterLisp 时学到了这些术语:它们可能早于那个,但现在似乎大多未使用。 CL 函数只能部分不传播 ((foo bar &rest more)
)。你的 foo
没有传播。
A spread/nospread 阻抗不匹配 是当你有扩展功能但想要一个不扩展的功能时,或者 反之亦然。它几乎总是设计问题的标志。 spread/nospread 问题的解决方法通常涉及 apply
.
你的问题是 foo
是一个 nospread 函数:它将它的所有参数变成一个列表,但你希望宏将它视为一个 spread 函数,将它传递给一个参数列表。
特别是,在 CL 中,对于任何 y
、函数或宏(但请参阅下文),像 (x (y ...))
这样的表达式永远不会变成 (x a1 a2 ...)
,这就是您需要发生。
在你的情况下,你想要像
这样的东西
(foo (expand bar a b ...)
变成
(foo (bar a) (bar b)
那不可能发生。
有一些 Lisps 有被称为 'splicing macros' 的东西,其中宏的扩展可以 'spliced' 到列表中,就像 ,@
对反引号所做的那样。可能是CL有拼接宏包,甚至可能是移植性好:*macroexpand-hook*
可以走很远的路。但是标准的 CL 宏不能做到这一点。
将你的 dsl 分成两部分可能是值得的,一个是你编程所针对的更简单的版本,另一个是对用户更友好的版本。例如:
(in-package #:impl)
(defun foo (&rest args) ...)
(defun bar (arg) ...)
(in-package #:dsl)
(defun foo (&rest args) (apply #'impl:foo (append args)))
(defun bar (&rest args) (mapcar #'impl:bar args))
(foo (bar 100 200 300 400 500))
抱歉标题笨拙,但我很难用几句话描述我要找的东西...
我正在做一个 Common Lisp DSL 项目,我想知道以下是否可行:
DSL 可能有几个功能
(defun foo (&rest rest))
和
(defun bar (arg))
将按以下方式使用:
(foo (bar 100) (bar 200) (bar 300) (bar 400) (bar 500))
等
现在有很多多余的输入,所以我想知道是否可以创建一个扩展宏 这将允许
(foo (expand bar 100 200 300 400 500))
不改变 foo
本身?
不需要宏:
(defun foo (&rest rest)
(apply #'+ rest)) ; for example add all together
(defun bar (arg)
(1+ arg)) ; 1+ is only an example here
(apply #'foo (mapcar #'bar '(100 200 300 400 500)))
不,如果不更改您正在定义的函数的签名(或者可能使用一些重新定义宏扩展的毛茸茸的东西),这是无法完成的:您有我称之为 'spread/nospread impedance mismatch' 的东西,它不能用 CL 中的标准宏解决。
'nospread' 函数是将所有参数包装成一个形式的函数。 'spread' 函数的每个参数都有一个形式。我在使用 InterLisp 时学到了这些术语:它们可能早于那个,但现在似乎大多未使用。 CL 函数只能部分不传播 ((foo bar &rest more)
)。你的 foo
没有传播。
A spread/nospread 阻抗不匹配 是当你有扩展功能但想要一个不扩展的功能时,或者 反之亦然。它几乎总是设计问题的标志。 spread/nospread 问题的解决方法通常涉及 apply
.
你的问题是 foo
是一个 nospread 函数:它将它的所有参数变成一个列表,但你希望宏将它视为一个 spread 函数,将它传递给一个参数列表。
特别是,在 CL 中,对于任何 y
、函数或宏(但请参阅下文),像 (x (y ...))
这样的表达式永远不会变成 (x a1 a2 ...)
,这就是您需要发生。
在你的情况下,你想要像
这样的东西(foo (expand bar a b ...)
变成
(foo (bar a) (bar b)
那不可能发生。
有一些 Lisps 有被称为 'splicing macros' 的东西,其中宏的扩展可以 'spliced' 到列表中,就像 ,@
对反引号所做的那样。可能是CL有拼接宏包,甚至可能是移植性好:*macroexpand-hook*
可以走很远的路。但是标准的 CL 宏不能做到这一点。
将你的 dsl 分成两部分可能是值得的,一个是你编程所针对的更简单的版本,另一个是对用户更友好的版本。例如:
(in-package #:impl)
(defun foo (&rest args) ...)
(defun bar (arg) ...)
(in-package #:dsl)
(defun foo (&rest args) (apply #'impl:foo (append args)))
(defun bar (&rest args) (mapcar #'impl:bar args))
(foo (bar 100 200 300 400 500))