【发布时间】:2016-10-09 12:18:09
【问题描述】:
我正在尝试编写一个可以以全局和嵌套方式使用的宏,如下所示:
;;; global:
(do-stuff 1)
;;; nested, within a "with-context" block:
(with-context {:foo :bar}
(do-stuff 2)
(do-stuff 3))
当以嵌套方式使用时,do-stuff 应该可以访问由with-context 设置的{:foo :bar}。
我已经能够像这样实现它:
(def ^:dynamic *ctx* nil)
(defmacro with-context [ctx & body]
`(binding [*ctx* ~ctx]
(do ~@body)))
(defmacro do-stuff [v]
`(if *ctx*
(println "within context" *ctx* ":" ~v)
(println "no context:" ~v)))
但是,我一直在尝试将 do-stuff 中的 if 从运行时转移到编译时,因为无论是从 with-context 的主体内还是全局调用 do-stuff 都是已经存在的信息在编译时可用。
不幸的是,我一直没能找到解决方案,因为嵌套宏似乎在多个“宏扩展运行”中得到扩展,所以*ctx*(在with-context 中设置)的动态绑定不可用当do-stuff 被扩展时,不再。所以这不起作用:
(def ^:dynamic *ctx* nil)
(defmacro with-context [ctx & body]
(binding [*ctx* ctx]
`(do ~@body)))
(defmacro do-stuff [v]
(if *ctx*
`(println "within context" ~*ctx* ":" ~v)
`(println "no context:" ~v)))
任何想法如何做到这一点?
或者我的方法完全是疯狂的,并且有一种模式可以通过这种方式将状态从一个宏传递到嵌套宏?
编辑:
with-context 的主体应该能够处理任意表达式,而不仅仅是do-stuff(或其他上下文感知函数/宏)。所以这样的事情也应该是可能的:
(with-context {:foo :bar}
(do-stuff 2)
(some-arbitrary-function)
(do-stuff 3))
(我知道some-arbitrary-function 是关于副作用的,例如它可能会向数据库写入一些内容。)
【问题讨论】:
标签: clojure macros nested state