Clojure.core 资料来源:为什么 ~@(反引号拼接运算符)在内部使用带引号的双列表,而不是 ~(反引号运算符)
Clojure.core source: Why ~@ (unquote-splicing operator) with a quoted double list inside, instead of ~ (unquote operator)
前言
我一直在查看 clojure.core
中的源代码,没有特别的原因。
我开始阅读 defmacro ns
,这里是删节的来源:
(defmacro ns
"...docstring..."
{:arglists '([name docstring? attr-map? references*])
:added "1.0"}
[name & references]
(let [...
; Argument processing here.
name-metadata (meta name)]
`(do
(clojure.core/in-ns '~name)
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))
(with-loading-context
~@(when gen-class-call (list gen-class-call))
~@(when (and (not= name 'clojure.core) (not-any? #(= :refer-clojure (first %)) references))
`((clojure.core/refer '~'clojure.core)))
~@(map process-reference references))
(if (.equals '~name 'clojure.core)
nil
(do (dosync (commute @#'*loaded-libs* conj '~name)) nil)))))
仔细观察
然后尝试阅读它,我看到了一些 st运行ge 宏模式,特别是我们可以看看:
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))
clojure.core
版本
这是从宏中提取的独立工作提取物:
(let [name-metadata 'name-metadata
name 'name]
`(do
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))))
=> (do (.resetMeta (clojure.lang.Namespace/find (quote name)) name-metadata))
当我运行 this could我忍不住想知道为什么在`((.resetMeta
点有一个双列表。
我的版本
我发现只要删除反引号拼接 (~@
) 就不需要双列表了。这是一个独立的工作示例:
(let [name-metadata 'name-metadata
name 'name]
`(do
~(when name-metadata
`(.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata))))
=> (do (.resetMeta (clojure.lang.Namespace/find (quote name)) name-metadata))
我的问题
那么,为什么clojure.core会选择这种看似极端运行的做事方式呢?
我自己的想法
这是约定俗成的产物吗?
是否还有其他以更复杂的方式使用它的类似实例?
~
总是发出一个表格; ~@
可能根本不发射任何东西。因此有时人们使用 ~@
有条件地拼接单个表达式:
;; always yields the form (foo x)
;; (with x replaced with its macro-expansion-time value):
`(foo ~x)`
;; results in (foo) is x is nil, (foo x) otherwise:
`(foo ~@(if x [x]))
这就是这里发生的事情:(.resetMeta …)
调用在 do
形式内发出,只有当 name-metadata
为真(非 false
, 非nil
).
在这种情况下,这并不重要——可以使用 ~
,去掉额外的括号并接受没有名称元数据的 ns
形式的宏扩展会有一个额外的nil
在 do
表格中。但是,为了更漂亮的扩展,使用 ~@
并且仅在实际有用时发出一个表单来处理名称元数据是有意义的。
前言
我一直在查看 clojure.core
中的源代码,没有特别的原因。
我开始阅读 defmacro ns
,这里是删节的来源:
(defmacro ns
"...docstring..."
{:arglists '([name docstring? attr-map? references*])
:added "1.0"}
[name & references]
(let [...
; Argument processing here.
name-metadata (meta name)]
`(do
(clojure.core/in-ns '~name)
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))
(with-loading-context
~@(when gen-class-call (list gen-class-call))
~@(when (and (not= name 'clojure.core) (not-any? #(= :refer-clojure (first %)) references))
`((clojure.core/refer '~'clojure.core)))
~@(map process-reference references))
(if (.equals '~name 'clojure.core)
nil
(do (dosync (commute @#'*loaded-libs* conj '~name)) nil)))))
仔细观察
然后尝试阅读它,我看到了一些 st运行ge 宏模式,特别是我们可以看看:
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))
clojure.core
版本
这是从宏中提取的独立工作提取物:
(let [name-metadata 'name-metadata
name 'name]
`(do
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))))
=> (do (.resetMeta (clojure.lang.Namespace/find (quote name)) name-metadata))
当我运行 this could我忍不住想知道为什么在`((.resetMeta
点有一个双列表。
我的版本
我发现只要删除反引号拼接 (~@
) 就不需要双列表了。这是一个独立的工作示例:
(let [name-metadata 'name-metadata
name 'name]
`(do
~(when name-metadata
`(.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata))))
=> (do (.resetMeta (clojure.lang.Namespace/find (quote name)) name-metadata))
我的问题
那么,为什么clojure.core会选择这种看似极端运行的做事方式呢?
我自己的想法
这是约定俗成的产物吗? 是否还有其他以更复杂的方式使用它的类似实例?
~
总是发出一个表格; ~@
可能根本不发射任何东西。因此有时人们使用 ~@
有条件地拼接单个表达式:
;; always yields the form (foo x)
;; (with x replaced with its macro-expansion-time value):
`(foo ~x)`
;; results in (foo) is x is nil, (foo x) otherwise:
`(foo ~@(if x [x]))
这就是这里发生的事情:(.resetMeta …)
调用在 do
形式内发出,只有当 name-metadata
为真(非 false
, 非nil
).
在这种情况下,这并不重要——可以使用 ~
,去掉额外的括号并接受没有名称元数据的 ns
形式的宏扩展会有一个额外的nil
在 do
表格中。但是,为了更漂亮的扩展,使用 ~@
并且仅在实际有用时发出一个表单来处理名称元数据是有意义的。