我已經將我的更大的問題減少爲使用file-io進行說明的人爲MVE(最小可行示例) 。我的問題涉及我在下面解釋的某個封裝宏 ;它不涉及更好的方式來使用file-io API; 我只是使用file-io來說明在一個小而簡單的 上下文中的宏觀問題。我的真實問題中的包裝宏觀策略很難顯示,並且解釋了這個問題,但是這個MVE抓住了問題的要點。Clojure中的擴展類型的包裝宏策略失敗
考慮以下方案:
(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
'宏調用(在CIDER中, 難以在此複製),並且它看起來沒問題。我不知道如何更深入地調試 ,但也許這裏有人能夠發現問題。
同樣,我知道這MVE已與文件IO的API更好的解決方案,但我真的 要調試的宏,沒有辦法避免使用它,因爲我需要 包裝宏觀策略在我的真實問題。
遇到過這樣的代碼,並且自己寫了一些代碼,我會敦促你用手寫出這些函數,即使它意味着重複你自己。如果你必須在幾年後重新訪問代碼,將來你會感謝你不必拆開嵌套層的宏。 – Alex