在Clojure中映射符号绑定宏(在fn中)
我定义了一个宏,将从字符串派生的符号绑定到字符串,如下所示:在Clojure中映射符号绑定宏(在fn中),clojure,macros,Clojure,Macros,我定义了一个宏,将从字符串派生的符号绑定到字符串,如下所示: lein repl ... Clojure 1.8.0 ... user=> (defmacro foo [s] `(def ~(symbol s) ~s)) #'user/foo (defn foo2 [s] `(def ~(symbol s) ~s)) (defmacro foos [ss] `(do ~@(map foo2 ss))) (foos ["asdf" "qwer"]) asdf ;; =>
lein repl
... Clojure 1.8.0 ...
user=> (defmacro foo [s] `(def ~(symbol s) ~s))
#'user/foo
(defn foo2 [s]
`(def ~(symbol s) ~s))
(defmacro foos [ss]
`(do ~@(map foo2 ss)))
(foos ["asdf" "qwer"])
asdf ;; => "asdf"
qwer ;; => "qwer"
在顶级调用时,它会按预期工作:
user=> (foo "asdf")
#'user/asdf
user=> asdf
"asdf"
但是,当我尝试将调用宏的函数映射到序列上时,宏会绑定函数参数符号,而不是我想要的符号:
user=> (map (fn [x] (foo x)) ["qwer"])
(#'user/x)
user=> x
"qwer"
user=> qwer
CompilerException ... Unable to resolve symbol: qwer ...
以下备选方案绑定Clojure创建的临时符号:
user=> (map #(foo %) ["qwer"])
(#'user/p1__1253#)
正如我在StackOverflow上研究的一些现有答案所建议的,当包装在doall
中时,它也不起作用
如何定义可以映射(在函数或其他方式中)到字符串集合的符号绑定宏?以下是一种方法。该解决方案首先显示宏
foo
的工作原理,然后使用带有函数的中间解决方案映射foo fn
,然后使用eval
最终的解决方案使用第二个宏映射foo mcr
。这似乎是必要的,因为(def…
是一种特殊形式。这与“一路向下的海龟”问题类似(但不完全相同),在“海龟”问题中,在一个地方使用宏要求所有调用者也都是宏,而不是函数
(ns clj.core
(:require
[tupelo.core :as t] ))
(t/refer-tupelo)
(defmacro foo
[arg]
`(def ~(symbol arg) ~arg))
(foo "aa")
(spyx aa)
(defn map-foo-fn
[coll]
(cons 'do
(forv [elem coll]
(list 'foo elem))))
(newline)
(prn (map-foo-fn ["bb"] ))
(eval (map-foo-fn ["bb"] ))
(spyx bb)
(defmacro map-foo-mcr
[coll]
`(do
~@(forv [elem coll]
(list 'foo elem))))
(newline)
(println (macroexpand-1 '(map-foo-mcr ["cc" "dd"] )))
(map-foo-mcr ["cc" "dd"] )
(spyx cc)
(spyx dd)
结果:
aa => "aa"
(do (foo "bb"))
bb => "bb"
(do (foo cc) (foo dd))
cc => "cc"
dd => "dd"
请记住,虽然宏可以做函数无法做的事情(避免arg求值),但宏不能做函数可以做的其他事情。特别是,宏不能传递给map
等需要高阶函数参数的地方
有关更多详细信息,请参阅并搜索“一直向下的宏”
请注意,project.clj
需要
:dependencies [
[tupelo "0.9.13"]
使spyx工作
map
是一个函数,foo
是一个宏。由于宏扩展发生在编译时,函数在运行时执行,因此不可能定义可以映射(从而在运行时扩展)的符号绑定宏
您可以这样做:
lein repl
... Clojure 1.8.0 ...
user=> (defmacro foo [s] `(def ~(symbol s) ~s))
#'user/foo
(defn foo2 [s]
`(def ~(symbol s) ~s))
(defmacro foos [ss]
`(do ~@(map foo2 ss)))
(foos ["asdf" "qwer"])
asdf ;; => "asdf"
qwer ;; => "qwer"
现在是另一种方式:使用函数map
和foo
展开宏