Clojure 使用变量参数使宏嵌套在宏中是可行的

Clojure 使用变量参数使宏嵌套在宏中是可行的,clojure,macros,Clojure,Macros,我陷入了一片混乱 背景:我的想法是定义一个“情境对象”(散列映射),它包含一个逻辑条件、一些文本和一个可变数量的“选项”。每个选项还有一个条件、一些文本和一些效果。将条件和效果转换为匿名函数,只保留文本 所以我想定义一个宏,将表达式转换成两个嵌套的hashmaps,包括一些生成的lambda函数,以便在运行时进行计算。我从中复制了一些制作makefn宏的想法,以便在其他宏中使用map,但这可能是错误的方法 守则: ; Two basic (silly maybe? let me know if

我陷入了一片混乱

背景:我的想法是定义一个“情境对象”(散列映射),它包含一个逻辑条件、一些文本和一个可变数量的“选项”。每个选项还有一个条件、一些文本和一些效果。将条件和效果转换为匿名函数,只保留文本

所以我想定义一个宏,将表达式转换成两个嵌套的hashmaps,包括一些生成的lambda函数,以便在运行时进行计算。我从中复制了一些制作
makefn
宏的想法,以便在其他宏中使用map,但这可能是错误的方法

守则:

; 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"))))
{:文本“选项2”,:效果#,:条件#}

{:文本“选项2”,:效果#,:条件#}

但当我评估
选项
情况
时,我得到:

(options ((< 1 5) "option1" (println "option 1 chosen"))
     ((= 1 2) "option2" (println "option 2 chosen")))
(选项((<15)“选项1”(选择println“选项1”))
((=12)“选项2”(选择打印项“选项2”))
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"))))
(def-sit)
(情境(<15)“标题”“描述”
(选项(>10 2)“选项1”(选择println“选项1”))
(选项(<15)“选项2”(选择打印选项2))

(在情况声明中明确声明选项)然后它就工作了。因此,我(理解)从宏内部调用宏是有问题的。有没有一种方法可以使它不直接使用
选项
宏(即从
情况
宏中调用它)?

确定,最后成功地使它工作

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

我有一个unquote和splice-unquote的混乱,在一个列表中传递内容,而不是作为一个内容列表,之后进行了评估,因此将bool转换为fn时出现了错误。

好的,终于成功了

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

我有一个unquote和splice-unquote的混乱,在一个列表中传递内容,而不是作为一个内容列表,这后来被评估,因此将bool转换为fn的错误。

情况
需要是一个宏,但它直接或间接调用的内容,最好是函数。我不确定我是否完全按照你说的,但我不认为是这样,因为你可以将宏扩展到其他宏,等等(就像在macroexpand中)@Thumbnail No,因为
情况
实际上没有调用任何这些函数;它正在扩展到给他们打电话。你可以重写一些东西,使这些helper宏成为函数,但没有必要。但我不明白
condify
lambdify
如何成为函数,因为它们需要宏所做的事情:生成代码而不计算代码。有没有办法用函数来实现这一点,在处理宏时这些函数是否可用?
情况
需要是一个宏,但它直接或间接调用的东西最好是函数。我不确定我是否完全按照你说的去做,但我认为情况并非如此,因为你可以将宏扩展到其他宏,等等(比如在macroexpand中)@Thumbnail No,因为
sition
实际上并没有调用这些函数;它正在扩展到给他们打电话。你可以重写一些东西,使这些helper宏成为函数,但没有必要。但我不明白
condify
lambdify
如何成为函数,因为它们需要宏所做的事情:生成代码而不计算代码。有没有一种方法可以让函数实现这一点?这些函数在处理宏时是否可用?
(def sit
 (situation (< 1 5) "title" "desc"
  (option (> 10 2) "option1" (println "option 1 chosen"))
  (option (< 1 5) "option2" (println "option 2 chosen"))))
(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)))