Макросы Clojure: передача отфильтрованной карты и арности другому макросу в зависимости от условия - PullRequest
0 голосов
/ 22 февраля 2019
(defmacro action1 [] `(prn "action1" ~'start ~'etype1))

(defmacro block [bindings & body] 
  `(let [~@(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) `'~v]) (cond
                     (map? bindings) bindings
                     (symbol? bindings) (var-get (resolve bindings))
                     :else (throw (Exception. "bindings must be map or symbol"))))]
       ~body))

(defmacro bar [ctx arity & expr]
      `(let [~'t1 "lv" ~'np (prn "bar_1st_let" '~ctx ~ctx '~arity ~arity '~arity(resolve (first '~arity)) )
            ] 
            (block ~ctx ;;~ctx = {start "s1" top "x"}
              (fn '~arity ;; '~arity = [etype1 cid1 id1 pl1]
                (let [~'__execonceresult 1]
                  (do ~@expr)     
                )
              )
            )
        )
  )

(defmacro foo_multi [metadata ctxv aritym & expr]
  `(let [~@(mapcat (fn [[k v]] [k `~v]) metadata) ~'np (prn "foo_multi_1st_let" '~aritym)] 
  (fn ~aritym 
      (for [~'ctx (filter #(= (% (some (fn [~'m] (if (= (name ~'m) "top") ~'m)) (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))
        ))))))

(def 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"))
   )

(let [myarr ["x" 100 200 {"p" 1 "q" 2}]] (apply foo myarr))

Невозможно передать арность из макроса бара в макрос блока и получить java.lang.NullPointerException.Остальная часть кода выполняется, если я комментирую вызов блока из макроса bar.

(defmacro bar [ctx arity & expr]
      `(let [~'t1 "lv" ~'np (prn "bar_1st_let" '~ctx ~ctx '~arity ~arity '~arity(resolve (first '~arity)) )
            ] 
            (comment block ~ctx ;;~ctx = {start "s1" top "x"}
              (fn '~arity ;; etype specific ~arity eg: [etype1 cid1 id1 pl1]
                (let [~'__execonceresult 1]
                  (do ~@expr) ;; uses etype1     
                )
              )
            )
        )
  )

После комментирования ниже выводятся строки отладки:

"foo_multi_1st_let" [etype a1 a2 a3]
"foo_multi_b4_case" {start "s1", top "x"} "x" ["x" 100 200 {"p" 1, "q" 2}]
"foo_multi_2nd_let" {start "s1", top "x"} "x" 100 200 {"p" 1, "q" 2}
"bar_1st_let" ctx {start "s1", top "x"} [etype1 cid1 id1 pl1] ["x" 100 200 {"p" 1, "q" 2}] [etype1 cid1 id1 pl1] nil
"foo_multi_b4_case" {start "s3", top "x"} "x" ["x" 100 200 {"p" 1, "q" 2}]
"foo_multi_2nd_let" {start "s3", top "x"} "x" 100 200 {"p" 1, "q" 2}
"bar_1st_let" ctx {start "s3", top "x"} [etype1 cid1 id1 pl1] ["x" 100 200 {"p" 1, "q" 2}] [etype1 cid1 id1 pl1] nil

Asсогласно отладочным строкам, напечатанным выше, в макросе бара я не могу разрешить первый символ арности, и он напечатан как ноль (не знаю причину почему).Цель состоит в том, чтобы правильно передать arity из макроса bar в блочный макрос и иметь возможность доступа и печати начала и значения etype1 в макросе action1.

Ответы [ 2 ]

0 голосов
/ 22 марта 2019

Как указал @ I0st3d, это может быть вашим решением после изменения определения бара, и foo_multi станет таким же, как и выше решение my-multi-let.

(defmacro action1 [] `(prn "action1" ~'start ~'etype))

(defn named-type? [m] (when (= (name m) "top") m))

(defmacro block [ctx & expr] 
  `(let [~@(mapcat (fn [[k v]] [k `~v]) ctx)] ~@expr))

(defmacro bar [bindings & body]
  `(block ~bindings (if (= ~'top ~'etype) (do 
     ~@body))))

(defmacro foo_multi [metadata bindings-list arity & body]
  (let [fns (->> bindings-list
       (map (fn [b] `(bar ~b ~arity ~@body))))
  ] `(block ~metadata (fn ~arity (do ~@fns)))
))

(def foo (foo_multi {meta1 "m1" meta2 "m2"} [{start "s1" top "x"} 
  {start "s2" top "y"}] [etype a1 a2 a3] 
(block {toc "c"} (block {c1 "d"} (action1)) "end")
))

(let [myarr ["x" 100 200 {"p" 1 "q" 2}]] (apply foo myarr))

В макросе бара вы получите все параметры arity.доступны, так что вы можете создать карту, варьирующуюся для конкретных символов etype, если это необходимо.ваш (пусть [myarr ["x" 100 200 {"p" 1 "q" 2}]] (применить foo myarr)) также будет работать как положено.

0 голосов
/ 25 февраля 2019

Я получаю 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]))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...