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,但我遇到了同样的错误,让我不知道如何解决它。
本教程由 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"))))
我不确定我在这里是否正确使用了宏,或者宏是否是解决此问题的正确策略。但是,关于在编写宏时遇到异常时该怎么做的一些想法或者调试它们的好的技术将不胜感激。
编辑: mikera 引起我的注意,这不是一个很好的例子,但我的问题仍然存在。因此,重申一下如果在 clojure 中编码宏时遇到异常,您会使用什么技术?
Basically I'm rather new to macros and I'm trying to work out how to write macros in Clojure. The problem is I keep getting exception errors and it's very difficult to figure out where to proceed. So really what I'm wondering is if I can get a list of either methods (or heuristics) for debugging Clojure macros.
Take the current problem I am working on, I have this model:
{:users [:name :email :registered-on]
:post [:title :author]}
and I want to convert it into the form:
(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"))))
for which I have written this macro:
(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)))))))
However when I run the macro I get:
java.lang.String cannot be cast to clojure.lang.IFn
[Thrown class java.lang.ClassCastException]
I've tried calling macroexpand-1, but I get the same error leaving me with little idea on how to solve it.
This tutorial provided by John Lawrence Aspden where he says "When the compiler sees a macro, which is just a function that returns some code, it runs the function, and substitutes the code that is returned into the program." prompted me to write the macro as a function which takes in the values I have and outputs the result that I want them to transform into.
Thus this works:
(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"))))
I'm not sure if I am using macros correctly here or if macros are the right strategy for solving this problem. However some ideas about what you do when faced with exceptions when writing a macro or good techniques for debugging them would be much appreciated.
EDIT: It has been brought to my attention by mikera that this is not a good example, however my question still stands. So to reiterate what techniques would you use when faced with an exception if coding a macro in clojure?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
就我个人而言,我不会为此使用宏 - 我建议的替代方案是:
(cashew.core/new-form ... )
(new-forms :users)
或类似操作即可从哈希映射中读取适当的表单。Personally I wouldn't use a macro for this - my proposed alternative would be:
(cashew.core/new-form ... )
for each form in the model as required(new-forms :users)
or similar to read the appropriate form out of the hashmap.