使用变量参数使宏嵌套在宏中

时间:2015-08-05 08:48:50

标签: clojure macros

我迷上了一团糟。

背景:我的想法是我定义一个情境对象' (哈希映射),包含逻辑条件,某些文本和可变数量的'选项'。每个选项还有一个条件,一些文本和一些效果。条件和效果转化为匿名函数,文本只是保留。

所以我想定义一个宏,将表达式转换为几个嵌套的哈希映射,包括一些生成的lambda函数,用于在运行时进行评估。我已经复制了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 ~@body)))
(defmacro condify [body] `(fn [] ~body))

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

; 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 ~@effs)))

(defmacro option-in-list
  [expr]
  `(hash-map :cond (condify ~(first expr))
             :text ~(second expr)
             :effects (lambdify ~@(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 ~@opt-list)))

我想从这种情况开始,所以如果我定义这个:

(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}}

我知道optionoption-in-list有效:

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

{:text&#34; option2&#34;,:effects#,:cond#}

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

{:text&#34; option2&#34;,:effects#,:cond#}

但是当我评估optionssituation时,我得到:

(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没有多少经验,而且我甚至不确定我是否会走向完全错误的方向。

修改

我设法制作了类似的作品,但我想知道为什么......

如果我这样做:

(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宏中调用它)?

1 个答案:

答案 0 :(得分:0)

好的,终于成功了。

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

(defmacro options
  [opts]
  `(list ~@(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)))

我有一个不引用和拼接 - 不引用的混乱,将事物放在列表中而不是作为事物列表,后来被评估,因此将bool转换为fn的错误。