什么时候在 Clojure 宏中使用 ~'some-symbol?
When to use ~'some-symbol in Clojure Macro?
当我阅读 The Joy of Clojure 时,我偶然发现了一些代码。
(fn [~'key ~'r old# new#]
(println old# " -> " new#)
此声明的确切行为是什么 ~'some-symbol
。
some-symbol#
和 '~another-symbol
或 gensym 之间的区别?
The Joy Of Clojure:(没看懂)
You’ll see the pattern ~'symbol
at times in Clojure
macros for selectively capturing a symbolic name in the body of a
macro. The reason for this bit of awkwardness[11] is that Clojure’s
syntax-quote attempts to resolve symbols in the current context,
resulting in fully qualified symbols. Therefore, ~'
avoids that
resolution by unquoting a quote.
您可以使用 the Literate Threading Macro 在 Tupelo 库中查看示例。我们希望用户键入符号 it
并让宏识别它。这是定义:
(defmacro it->
"A threading macro like as-> that always uses the symbol 'it'
as the placeholder for the next threaded value "
[expr & forms]
`(let [~'it ~expr
~@(interleave (repeat 'it) forms)
]
~'it))
这也称为 "anaphoric" 宏。然后用户创建如下代码:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )
用户在他们的代码中包含特殊符号 it
,这是宏所期望的(& 在这种情况下是必需的)。
这有点特殊。在大多数情况下,无论用户选择什么符号,您都希望宏起作用。这就是为什么大多数宏使用 (gensym...)
或带有“#”后缀的 reader 版本的原因,就像这个例子:
(defmacro with-exception-default
"Evaluates body & returns its result. In the event of an exception, default-val is returned
instead of the exception."
[default-val & body]
`(try
~@body
(catch Exception e# ~default-val)))
这是 "normal" 的情况,其中宏创建一个 "local variable" e#
保证不与任何用户符号重叠。一个类似的示例显示 spyx
宏创建一个名为 spy-val#
的 "local variable" 来临时保存表达式 expr
:
的计算结果
(defmacro spyx
"An expression (println ...) for use in threading forms (& elsewhere). Evaluates the supplied
expression, printing both the expression and its value to stdout, then returns the value."
[expr]
`(let [spy-val# ~expr]
(println (str (spy-indent-spaces) '~expr " => " (pr-str spy-val#)))
spy-val#))
请注意,对于 (println...)
语句,我们看到了与 '~expr
相反的语法——但这是另一天的话题。
当我阅读 The Joy of Clojure 时,我偶然发现了一些代码。
(fn [~'key ~'r old# new#]
(println old# " -> " new#)
此声明的确切行为是什么 ~'some-symbol
。
some-symbol#
和 '~another-symbol
或 gensym 之间的区别?
The Joy Of Clojure:(没看懂)
You’ll see the pattern
~'symbol
at times in Clojure macros for selectively capturing a symbolic name in the body of a macro. The reason for this bit of awkwardness[11] is that Clojure’s syntax-quote attempts to resolve symbols in the current context, resulting in fully qualified symbols. Therefore,~'
avoids that resolution by unquoting a quote.
您可以使用 the Literate Threading Macro 在 Tupelo 库中查看示例。我们希望用户键入符号 it
并让宏识别它。这是定义:
(defmacro it->
"A threading macro like as-> that always uses the symbol 'it'
as the placeholder for the next threaded value "
[expr & forms]
`(let [~'it ~expr
~@(interleave (repeat 'it) forms)
]
~'it))
这也称为 "anaphoric" 宏。然后用户创建如下代码:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )
用户在他们的代码中包含特殊符号 it
,这是宏所期望的(& 在这种情况下是必需的)。
这有点特殊。在大多数情况下,无论用户选择什么符号,您都希望宏起作用。这就是为什么大多数宏使用 (gensym...)
或带有“#”后缀的 reader 版本的原因,就像这个例子:
(defmacro with-exception-default
"Evaluates body & returns its result. In the event of an exception, default-val is returned
instead of the exception."
[default-val & body]
`(try
~@body
(catch Exception e# ~default-val)))
这是 "normal" 的情况,其中宏创建一个 "local variable" e#
保证不与任何用户符号重叠。一个类似的示例显示 spyx
宏创建一个名为 spy-val#
的 "local variable" 来临时保存表达式 expr
:
(defmacro spyx
"An expression (println ...) for use in threading forms (& elsewhere). Evaluates the supplied
expression, printing both the expression and its value to stdout, then returns the value."
[expr]
`(let [spy-val# ~expr]
(println (str (spy-indent-spaces) '~expr " => " (pr-str spy-val#)))
spy-val#))
请注意,对于 (println...)
语句,我们看到了与 '~expr
相反的语法——但这是另一天的话题。