在Clojure中对扩展类型的包装宏观策略失败

时间:2016-10-01 16:40:26

标签: clojure macros

我把我的更大问题减少为人工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'和macroexpand-1'宏调用(在CIDER中, 很难在这里复制),看起来还不错。我不知道如何调试 更深层次,但也许这里有人能发现问题。

同样,我知道这个MVE有更好的文件io API解决方案,但我真的 想要调试宏,没有找到避免使用它的方法,因为我需要 在我真正的问题中包装宏观策略。

1 个答案:

答案 0 :(得分:1)

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

如果你想走这条路,我想你需要编写一个宏,它将产生所需的expand-type形式,所有的功能体已经扩展到位。