我得到一个由你引起的 NullPointeraction1
宏返回nil
和block
宏尝试执行响应action1
。拼接引号可以解决这个问题。另外,在我看来,绑定值上有太多引号block
,所以我也把它们拿出来了。
(defmacro block [bindings & body]
(let [bs (->>
(cond
(map? bindings) bindings
(symbol? bindings) []
:else (throw (Exception. "bindings must be map or symbol")))
(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) v])))]
`(let [~@bs]
~@body)))
第二,clojure.core/resolve
will 只在命名空间中查找变量 https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/ns-resolve,不是由以下人员创建的本地人clojure.core/let
or clojure.core/fn
。因此,如果您尝试解决本地问题,您将得到nil
.
(defmacro bar [bindings arity & expr]
`(block ~bindings ;;~bindings = {start "s1" top "x"}
(fn ~arity ;; '~arity = [etype1 cid1 id1 pl1]
(let [~'__execonceresult 1]
(do ~@expr)))))
(macroexpand-1 '(bar {start "s1" top "x"} [etype1 cid1 id1 pl1] (action1)))
;; =>
(do
(user/block
{start "s1", top "x"}
(clojure.core/fn
[etype1 cid1 id1 pl1]
(clojure.core/let [__execonceresult 1] (do (action1))))))
所以这部分foo_multi
现在运行。
(block {toc "c"} (block {c1 "d"} (action1)) "end")
;;=>
"action1" :start :etype1
"end"
In foo_multi
:
(defn named-top? [m]
(when (= (name m) "top")
m))
(defmacro foo_multi [metadata ctxv aritym & expr]
(prn "foo_multi" (map #(get % (some named-top? (keys %))) ctxv))
`(let [~@(mapcat (fn [[k v]] [k v]) metadata)]
(prn "foo_multi_1st_let" '~aritym)
(fn ~aritym
(for [~'ctx (filter #(= (get % (some named-top? (keys %))) ~'etype) '~ctxv)]
(do #_ (prn "foo_multi_b4_case" ~'ctx ~'etype ~aritym)
(case ~'etype
"x"
(let [[~'etype1 ~'cid1 ~'id1 ~'pl1] ~aritym ~'np (prn "foo_multi_2nd_let" ~'ctx ~'etype1 ~'cid1 ~'id1 ~'pl1)]
(bar ~'ctx [~'etype1 ~'cid1 ~'id1 ~'pl1] ~@expr))
"y"
(let [[~'etype2 ~'cid2 ~'id2 ~'pl2] ~aritym]
(bar ~'ctx [~'etype2 ~'cid2 ~'id2 ~'pl2] ~@expr))))))))
过滤器(filter #(= (get % (some named-top? (keys %))) ~'etype) '~ctxv)
似乎会出错,因为etype
除非被迫存在,否则不会存在metadata
争论。用~'idiom
因为你永远不知道你在跟踪什么,而这只是远距离的幽灵行动。最好使用gensym
的特征syntax-quote
local#
as 此处描述 https://clojure.org/guides/weird_characters#syntax_quote.
正如对调试策略的评论一样,尝试提取简化的最小案例可能会帮助您了解正在发生的情况。我认为这段代码相当混乱。有一大堆东西拼凑在一起。听起来好像您正在学习 Clojure 和宏,并且一口气咬掉了太多内容。我认为您正在尝试使用这些宏复制词法范围,但我不完全确定最终目标是什么。也许读完this https://clojure.org/reference/special_forms会有帮助的。
另外,我怀疑当你发现这一点时你会遇到麻烦clojure.core/for
很懒。
foo_multi
返回一个返回函数列表的函数。因此,要实际执行您编写的大部分代码,您需要调用这些函数。
(let [start :start
etype1 :etype1
foo (foo_multi {meta1 "m1" meta2 "m2" }
[{start "s1" top "x"}
{start "s3" top "x"}
{start "s2" top "y"}]
[etype a1 a2 a3]
(block {toc "c"}
(block {c1 "d"} (action1))
"end"))
args ["x" 100 200 {"p" 1 "q" 2}]
fns (apply foo args)]
(map #(apply % args) fns))
如果您尝试使用映射而不是向量来复制词法范围,也许此代码片段将帮助您重新思考您的方法:
(defmacro my-let [bindings & body]
(let [bs (vec (mapcat (fn [[k v]] [k v]) bindings))]
`(let ~bs
~@body)))
(defmacro my-multi-let [bindings-list & body]
(->> bindings-list
(map (fn [b] `(my-let ~b ~@body)))
(cons `list)))
(macroexpand-1 '(my-let {a "a1" b "b1"} [a b]))
(macroexpand-1 '(my-multi-let [{a "a1" b "b1"} {a "a2" b "b2"}] [a b]))