这个问题与两个宏之间的相互作用有关,一个宏包含在另一个宏中。所描述的两个宏是说明交互的示例,而不是唯一的例子。
考虑一个简单的clojure宏,它会产生副作用,但不会改变其输入。
(defmacro echo [action & args]
`(fn [tru#] (do (~action ~@args tru#) tru#)))
用法
((echo log/info "result") :foo)
将宏包含在另一个宏中,例如 - >>宏。 我的直觉说如下:
(->> :foo (echo log/info "result") clojure.pprint/pprint)
这不适用于 - >>宏首先插入:foo,它使echo宏留有错误的参数。 解决问题的一种方法是引入额外的括号,但这对我来说似乎很难看:
(->> :foo ((echo log/info "result")) clojure.pprint/pprint)
丑陋我的意思是它闻起来像c宏。
答案 0 :(得分:4)
这里没有什么令人惊讶的事情,但我认为echo
扩展到一个功能正在使它更难理解。 macroexpand
可以帮助说明正在发生的事情:
(macroexpand '(->> :foo (echo prn "result") clojure.pprint/pprint))
=> (clojure.pprint/pprint (echo prn "result" :foo))
在这里你可以看到->>
正确地将参数线程化到echo
表单的第二个/最后一个位置,这不是你想要的,因为echo
扩展为一个函数必须调用,:foo
作为唯一参数。如果我们扩展你的另一个例子:
(macroexpand '(->> :foo
((echo prn "result"))
clojure.pprint/pprint))
=> (clojure.pprint/pprint ((echo prn "result") :foo))
您可以在线程化后看到,echo
来电形成为((echo prn "result") :foo)
,就像上面的工作示例一样。这里的宏之间没有奇怪的交互,只是语法混乱,因为echo
扩展为必须调用的函数。
如果将echo
扩展为代码而不是匿名函数,我认为这将更清晰/更清晰(从宏扩展的角度来看)。但是我认为如果没有宏,你可以达到同样的效果,这通常是可取的:
(defn echo [f x & args]
(do (apply f x args)
(last args))
(->> :foo
(echo prn "result")
(clojure.pprint/pprint))
;; "result" :foo
;; :foo
;; => nil
虽然还有其他方法可以“窥探”线程宏中的值。试试这个例子:
(-> :foo
(doto (prn "result"))
(clojure.pprint/pprint))
;; :foo "result"
;; :foo
;; => nil