解决Clojure中常见宏错误的方法

时间:2011-05-08 21:23:09

标签: macros clojure

基本上我对宏很新,我正在尝试研究如何在Clojure中编写宏。问题是我一直遇到异常错误,很难弄清楚从哪里开始。所以我真的想知道的是,如果我能得到一个用于调试Clojure宏的方法(或启发式)的列表。

采取我正在处理的当前问题,我有这个模型:

{:users [:name :email :registered-on]
 :post [:title :author]}

我希望将其转换为以下形式:

(do (def new-form-users (cashew.core/new-form "/users/new" "Create a new Users"
         ["name" "email" "registered-on"] ("Name" "Email" "Registered-on"))) 
    (def new-form-post (cashew.core/new-form "/post/new" "Create a new Post"
         ["title" "author"] ("Title" "Author"))))

我写了这个宏:

(defmacro gen-create-forms [model]
               `(do
                 ~@(for [[entity-kw values] model]
                        (let [entity-sym (-> entity-kw name capitalize)
                             fields (vec (map name values))]
                          `(def ~(symbol (str "new-form-" (name entity-kw))) (new-form ~(str "/" (name entity-kw) "/new") ~(str "Create a new " entity-sym) ~fields ~(map capitalize fields)))))))

然而,当我运行宏时,我得到:

java.lang.String cannot be cast to clojure.lang.IFn
  [Thrown class java.lang.ClassCastException]

我已经尝试过调用macroexpand-1,但是我得到了同样的错误,让我对如何解决它一无所知。

This tutorial provided by John Lawrence Aspden他说:“当编译器看到一个宏时,它只是一个返回某些代码的函数,它运行该函数,并替换返回到程序中的代码。”促使我把宏写成一个函数,它接收我拥有的值并输出我希望它们转换成的结果。

因此这有效:

(defn gen-create-forms [model]
               `(do
                 ~@(for [[entity-kw values] model]
                        (let [entity-sym (-> entity-kw name capitalize)
                             fields (vec (map name values))]
                          `(def ~(symbol (str "new-form-" (name entity-kw))) (new-form ~(str "/" (name entity-kw) "/new") ~(str "Create a new " entity-sym) ~fields ~(map capitalize fields)))))))

(gen-create-forms {:users [:name :email :registered-on]
              :post [:title :author]})
(do (def new-form-users (cashew.core/new-form "/users/new" "Create a new Users" ["name" "email" "registered-on"] ("Name" "Email" "Registered-on"))) (def new-form-post (cashew.core/new-form "/post/new" "Create a new Post" ["title" "author"] ("Title" "Author"))))

我不确定我是否在这里正确使用宏,或者宏是否是解决此问题的正确策略。然而,在编写宏或遇到异常时你会做些什么的一些想法非常感谢用于调试它们的良好技术。

编辑:迈克拉已经引起我的注意,这不是一个好例子,但我的问题仍然存在。那么重申如果在clojure中编写一个宏时遇到异常会使用什么技术?

1 个答案:

答案 0 :(得分:2)

就我个人而言,我不会使用宏 - 我建议的替代方案是:

  • 避免尝试在命名空间中生成新的符号名称 - 这可能会变得混乱和复杂!
  • 而是创建一个名为“new-forms”的单个数据结构,其中包含hashmap中的所有表单。您可以使用关键字或字符串作为键,我个人会使用“:users”之类的关键字,因为您已经在模型数据结构中使用该方法。
  • 使用将模型作为参数的函数生成“new-forms”,并根据需要为模型中的每个表单调用(cashew.core/new-form ... )
  • 当您想要访问特定表单时,您可以只执行(new-forms :users)或类似操作,从哈希映射中读取相应的表单。