【问题标题】:Methods for solving common macro errors in ClojureClojure 中常见宏错误的解决方法
【发布时间】:2011-05-08 21:23:08
【问题描述】:

基本上我对宏比较陌生,我正在尝试研究如何在 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"))))

我不确定我在这里是否正确使用了宏,或者宏是否是解决此问题的正确策略。 但是,关于在编写宏或遇到异常时如何处理的一些想法调试它们的好技术将不胜感激。

编辑:mikera 已经引起我的注意,这不是一个很好的例子,但是我的问题仍然存在。因此,重申一下如果在 clojure 中编写宏,当遇到异常时您会使用哪些技术?

【问题讨论】:

    标签: macros clojure


    【解决方案1】:

    我个人不会为此使用宏 - 我建议的替代方案是:

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

    【讨论】:

    • 那么一个包含我定义的所有函数的嵌套数据结构?即: (def newform :users {:new # :read #} :post {:new # :read #})?仅仅为这些生成的表单创建一个新的命名空间有什么不同/它提供了什么优势?然而,我承认我使用了一个糟糕的例子,但它仍然没有回答我的问题。不是我太介意,这个还是蛮有用的!
    • 是的 - 我认为嵌套数据结构是最好的。我倾向于仅将名称空间用于“我在源代码中手动定义的东西”。因此,我以编程方式创建/生成的任何内容通常都会放入数据结构中。这实际上只是个人喜好(命名空间实际上是一种底层映射!),但我发现这有助于避免命名空间中的混淆、维护问题和可能的冲突。
    • 你知道@mikera 我真的开始欣赏这个评论了,非常感谢这个想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-07
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 2020-10-01
    • 2011-04-06
    • 2011-12-18
    相关资源
    最近更新 更多