如何在Clojure宏展开阶段获取另一宏的元数据?
宏展开阶段读取Clojure宏的元数据
在个人项目中定义了多个Clojure宏,希望在宏展开阶段读取其他宏定义的元数据,以此让当前宏根据输入形式做不同展开。虽然展开后读取元数据的方案更简单,但想探索宏展开阶段实现的可能性。
示例宏定义
(defmacro stackoverflow {:example 23} [& args] (comment "This is just an example"))
非宏展开阶段的可行读取方式
普通函数读取宏元数据
(defn- -example? [form-var] (-> form-var meta :example some?)) ;; 调用结果 => (-example? #'stackoverflow) true
普通宏读取元数据
(defmacro example?-macro[form] `(-example? (var ~form))) ;; 调用结果 => (example?-macro stackoverflow) true
宏展开阶段读取的问题
尝试直接在宏展开阶段判断时出现错误:
(defmacro test-example? [form] (if (example?-macro form) (println "It is indeed an example") (println "It is not an example!"))) ;; 错误信息 > Unable to resolve var: form in this context
核心需求:在宏内不展开输入宏的情况下,在宏展开阶段获取其var的元数据,期望的理想实现类似:
;; 期望的行为 (defmacro imagining-solution [form] (if (:example `(meta (var ~form))) (println "It is indeed an example") (println "It is not an example!")))
宏展开阶段读取元数据的实现方法
要在宏展开阶段获取目标宏的元数据,关键是在宏的展开代码中,通过resolve函数在编译期找到目标var,再读取其元数据。resolve需要传入符号,因此要确保输入的form是一个符号:
(defmacro test-example? [form] (let [target-var (resolve form)] (if (-> target-var meta :example some?) (println "It is indeed an example") (println "It is not an example!")))) ;; 调用测试 (test-example? stackoverflow) ;; 输出:It is indeed an example
补充说明
resolve在宏展开阶段(编译期)执行,直接查找当前环境中名为form的var,无需等到运行时- 需确保
form是一个符号(如stackoverflow),若输入是复杂形式,需先做符号提取处理 - 该方式不会展开目标宏,仅读取其var的元数据,完全符合宏展开阶段处理的需求
内容的提问来源于stack exchange,提问作者Roman Makhlin
相关产品推荐
相关产品推荐

