在Clojure

时间:2016-12-27 22:16:54

标签: clojure macros

我定义了一个宏来将从字符串派生的符号绑定到字符串,如下所示:

lein repl
... Clojure 1.8.0 ...
user=> (defmacro foo [s] `(def ~(symbol s) ~s))
#'user/foo

在顶级调用时,它按预期工作:

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中包含它时也不起作用。

如何定义一个符号绑定宏,我可以在一个字符串集合上映射(在函数中或其他方面)?

2 个答案:

答案 0 :(得分:3)

map是一个函数,foo是一个宏。由于宏扩展在编译时发生并且函数在运行时执行,因此定义一个可以映射(因此在运行时扩展)的符号绑定宏是不可能的。

你可以做的是这样的事情:

(defn foo2 [s]
  `(def ~(symbol s) ~s))

(defmacro foos [ss]
  `(do ~@(map foo2 ss)))

(foos ["asdf" "qwer"])

asdf ;; => "asdf"
qwer ;; => "qwer"

现在反过来了:使用函数mapfoo扩展宏。

答案 1 :(得分:2)

这是一种做法。解决方案首先显示 foo如何工作,然后使用带有 功能的中间解决方案 map-foo-fn然后eval

最终解决方案使用第二个 map-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评估),但宏 不能 < / strong>做其他功能可以做的事情。特别是,宏不能传递给map ,其中需要更高阶函数参数。

有关详细信息,请参阅http://www.braveclojure.com/writing-macros并搜索“Macros All Way Down”

请注意project.clj需要

:dependencies [
    [tupelo "0.9.13"]

spyx工作