【问题标题】:Wrapper-macro tactic over extend-type in Clojure failedClojure 中扩展类型的包装宏策略失败
【发布时间】:2016-10-01 16:40:25
【问题描述】:

我已将更大的问题简化为人工 MVE(最小可行示例) 使用 file-io 进行说明。我的问题涉及某个包装宏 我在下面解释;它不涉及使用 file-io API 的更好方法; 我只是用file-io小而简单地说明宏问题 语境。我真正的问题中的包装宏策略更难展示和 解释一下,但这个 MVE 抓住了问题的要点。

考虑以下协议:

(defprotocol Dumper
  (dump [this]))

以及对java.io.File的实现

(extend-type java.io.File
  Dumper
  (dump [file]
    (with-open [rdr (io/reader file)]
      (doseq [line (line-seq rdr)]
        (println line)))))

我们在哪里完成了(:use [clojure.java.io :as io]) 以获得reader 功能。我可以按如下方式使用它:

(defn -main
  [& args]
  (dump (io/file "resources/a_file.txt")))
Hello from a text file.

现在,我想创建该协议的另一个实现,这次结束 java.lang.String。此实现包装字符串,将其视为 文件路径字符串;创建一个clojure.java.io/file;然后调用另一个 协议的实现:

(extend-type java.lang.String
  Dumper
  (dump [path-str] (-> path-str, io/file, dump)))

然后这样称呼它:

(defn -main
  [& args]
  (dump (io/file "resources/a_file.txt"))
  (dump          "resources/a_file.txt"))
Hello from a text file.
Hello from a text file.

在我的实际问题中,协议中有很多功能,其中一个 实现只是以所示的方式包装另一个。请注意,在 包装器实现,方法名称 dump 被复制。让我们消除 使用宏进行复制(当真正的协议有很多 方法):

(defmacro wrap-path-string [method]
  `(~method [path-str] (-> path-str, io/file, ~method)))

(extend-type java.lang.String
  Dumper
  (wrap-path-string dump))

糟糕,编译器不喜欢它:

Exception in thread "main" java.lang.UnsupportedOperationException: 
  nth not supported on this type: Symbol, compiling:(wrapper_mve/core.clj:18:1)
at clojure.lang.Compiler.analyze(Compiler.java:6688)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$MapExpr.parse(Compiler.java:3072)

我尝试了 macroexpand-all'ing 和 macroexpand-1'ing 宏调用(在 CIDER 中, 在这里很难复制),看起来还不错。我不知道如何调试 更深,但也许这里有人可以发现问题。

再次,我知道这个 MVE 有更好的文件 io API 解决方案,但我真的 想调试宏,而不是想办法避免使用它,因为我需要 我真正的问题中的包装宏策略。

【问题讨论】:

  • 在野外遇到过这样的代码,并且自己编写了一些代码,我强烈建议您手动编写函数,即使这意味着重复自己。如果您必须在几年后重新访问代码,将来您会感谢您不必解压缩嵌套的宏层。

标签: clojure macros


【解决方案1】:

我认为问题在于extend-type 本身就是一个宏,并且宏扩展从最外层的形式开始(与函数评估相反,它在调用函数之前评估每个参数)。在这种情况下,extend-type 的宏扩展试图将 (wrap-path-string dump) 形式视为函数体,并期望第二项是 arg 向量但找到符号 dump

如果你想走这条路,我认为你需要编写一个宏来生成所需的expand-type 表单,其中所有函数体都已经展开。

【讨论】:

  • 我也很怀疑。感谢您的确认。你上面的建议也很好。这可能是非 DRY 是正确答案的情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-25
  • 1970-01-01
相关资源
最近更新 更多