Я получаю NullPointer, вызванный вашим макросом action1
, возвращающим 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
будет ищите только переменные в пространстве имен , а не локальные, созданные clojure.core/let
или 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"
В 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#
как , описанную здесь .
Так же, как комментарий к стратегии отладки, попытка извлечь упрощенный минимальный случай может иметьпомог вам понять, что происходит.Я думаю, этот код довольно запутан.Там куча всего взломанного вместе.Похоже, что вы склоняетесь к Clojure и макросам и слишком много откусываете за один раз.Я думаю, что вы пытаетесь скопировать лексическую область действия с этими макросами, но я не совсем уверен, какова конечная цель.Возможно, чтение поможет .
Кроме того, я подозреваю, что у вас возникнут проблемы, когда вы обнаружите, что 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]))