2015-08-05 3 views
1

Я запутался в макро беспорядок.Выполнение макроса, вложенного в макросы с использованием переменных аргументов

Предпосылки: Идея состоит в том, что я определяю «ситуационный объект» (хэш-карту), который содержит логическое условие, некоторый текст и переменное число «параметров». У каждого варианта есть условие, некоторый текст и некоторые эффекты. Условия и эффекты превращаются в анонимные функции, текст просто сохраняется.

Так что я хочу определить макрос, который преобразует выражение в пару вложенных хэш-карт, включая некоторые сгенерированные лямбда-функции для оценки во время выполнения. Я скопировал некоторые идеи из here для изготовления make-fn макроса использовать карту внутри других макросов, но, возможно, это неправильный путь ...

Код:

; Two basic (silly maybe? let me know if I'm reinventing the wheel) 
; macros for turning conditions and effects into functions. 
(defmacro lambdify [& body] `(fn [] (do [email protected]))) 
(defmacro condify [body] `(fn [] ~body)) 

(defmacro make-fn [m] 
    `(fn [& args#] 
    (eval `(~'~m [email protected]#)))) 

; option and option-in-list do the same but one takes a single parameter 
; as a list. I'm trying out different things. 
(defmacro option 
    [cnd text & effs] 
    `(hash-map :cond (condify ~cnd) 
      :text ~text 
      :effects (lambdify [email protected]))) 

(defmacro option-in-list 
    [expr] 
    `(hash-map :cond (condify ~(first expr)) 
      :text ~(second expr) 
      :effects (lambdify [email protected](next (next expr))))) 

(defmacro options 
    [& opts] 
    `(map (make-fn option-in-list) ~opts)) 

(defmacro situation 
    [conds title text & opt-list] 
    `(hash-map :cond (condify ~conds) 
       :title ~title 
       :text ~text :opts (options [email protected]))) 

Я хочу начать с ситуация, так что если я определяю так:

(situation (< 1 5) "title" "desc" 
    ((> 1 2) "option1" (println "option 1 chosen")) 
    ((< 1 5) "option2" (println "option 2 chosen")))) 

Я ожидаю, чтобы иметь хэш-карту как

{ :cond (.. the fn for (< 1 5)), 
    :title "title" , 
    :text "desc", 
    :opts [{:cond (the fn for (> 1 2)) 
      :text "option1" 
      :effects: (the fn for println...)} 
      { .. the second option also as a hash}} 

Я знаю, что option и option-in-list работы:

(option (= 1 2) "option2" (println "option 2 chosen")) 

{Текст "option2",: эффекты #,: конд #}

(option-in-list ((= 1 2) "option2" (println "option 2 chosen"))) 

{Текст «option2 ",: effects #,: cond #}

Но когда я ева luate либо options или situation, я получаю:

(options ((< 1 5) "option1" (println "option 1 chosen")) 
    ((= 1 2) "option2" (println "option 2 chosen"))) 

java.lang.ClassCastException: java.lang.Boolean не может быть приведен к clojure.lang.IFn

Как я понимаю, это означает, что что где-то он пытается оценить список, имеющий логическое значение, поскольку он является первым членом, и поэтому он пытается передать его функции, правильно?

Но я не могу найти, где (или как) это происходит.

Любое руководство будет оценено по достоинству, у меня нет большого опыта работы с Clojure, и я даже не уверен, пойду ли я в совершенно неправильном направлении.

EDIT

мне удалось сделать что-то подобное, что работает, но я хотел бы знать, почему ..

Если я это сделать:

(def sit 
(situation (< 1 5) "title" "desc" 
    (option (> 10 2) "option1" (println "option 1 chosen")) 
    (option (< 1 5) "option2" (println "option 2 chosen")))) 

(объявлении явно вариант в заявлении о ситуации), тогда он работает.Итак, что-то не так в моем (понимании) вызова макросов из макросов. Есть ли способ заставить его работать без непосредственного использования макроса option (т. Е. Вызывая его из макроса situation)?

+0

«ситуация» должна быть макросом, но то, что она называет, прямо или косвенно, должно быть лучше. – Thumbnail

+0

Я не уверен, что я точно слежу за тем, что вы говорите, но я не думаю, что это так, так как вы можете иметь макросы, которые расширяются до других макросов и т. Д. (Например, в макроэксплуатации) – Sebastian

+0

@ Thumbnail Нет, потому что «ситуация» фактически не вызывает какую-либо из этих функций; он распространяется на призыв к ним. Вы можете переписать все так, чтобы эти вспомогательные макросы были функциями, но нет необходимости. – amalloy

ответ

0

Хорошо, наконец, удалось заставить его работать.

(defmacro option 
    [expr] 
    `(hash-map :cond (condify ~(first expr)) 
      :text ~(second expr) 
      :effects (lambdify [email protected](next (next expr))))) 

(defmacro options 
    [opts] 
    `(list [email protected](map (make-fn option) opts))) 

(defmacro situation 
    [conds title text & opt-list] 
    `(hash-map :cond (condify ~conds) 
       :title ~title 
       :text ~text 
       :opts (options ~opt-list))) 

Я имел Unquote и сращивание-Unquote беспорядка, передавая вещи в списке, а не как список вещей, который впоследствии был оценены и, следовательно, ошибки для литья логического значения как Fn.

Смежные вопросы