在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
展开宏