开发者

Methods for solving common macro errors in 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 value开发者_如何学运维s] 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?


Personally I wouldn't use a macro for this - my proposed alternative would be:

  • Avoid trying to generate new symbol names in the namespace - this can get messy and complicated!
  • Instead create a single data structure called "new-forms" that contains all the forms in a hashmap. You could use either keywords or strings as keys, personally I'd use keywords like ":users" since you are already using that approach for your model data structure.
  • Generate "new-forms" with a function that takes the model as a parameter and calls (cashew.core/new-form ... ) for each form in the model as required
  • When you want to acess a specific form, you can then just do (new-forms :users) or similar to read the appropriate form out of the hashmap.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜