开发者

Methods for solving common macro errors in Clojure

开发者 https://www.devze.com 2023-03-04 16:56 出处:网络
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.

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

精彩评论

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