在 Clojure 中试用资源

Try-with-resources in Clojure

Clojure 是否具有 Java 的 try-with-resources 构造的等效项?

如果不是,在 Clojure 代码中处理这个习语的正常方法是什么?

Java-7 之前用于安全打开和关闭资源的习语非常冗长,以至于他们实际上为语言添加了对 try-with-resources 的支持。我在标准 Clojure 库中找不到这个用例的宏,这对我来说似乎很奇怪。

一个主流的基于 Clojure 的项目存储库的例子——展示这个问题在实践中是如何处理的——会很有帮助。

您可以使用 with-open 将资源绑定到符号,并确保在控制流离开块后关闭资源。

以下示例来自 clojuredocs。

(with-open [r (clojure.java.io/input-stream "myfile.txt")] 
     (loop [c (.read r)] 
       (when (not= c -1)
         (print (char c)) 
         (recur (.read r)))))

这将扩展为以下内容:

(let [r (clojure.java.io/input-stream "myfile.txt")] 
  (try
    (loop [c (.read r)] 
      (when (not= c -1)
        (print (char c)) 
        (recur (.read r))))
    (finally (.close r))))

您可以看到 let 块是通过 try-finally 调用 .close() 方法创建的。

你可以做一些更接近 java 的事情,在 with-open 之上构建一些宏。它可能看起来像这样:

(defmacro with-open+ [[var-name resource & clauses] & body]
  (if (seq clauses)
    `(try (with-open [~var-name ~resource] ~@body)
          ~@clauses)
    `(with-open [~var-name ~resource] ~@body)))

因此您可以在绑定的同时传递附加子句。

(with-open+ [x 111]
  (println "body"))

扩展为简单 with-open:

(let*
  [x 111]
  (try (do (println "body")) (finally (. x clojure.core/close))))

而其他子句导致将其包装在 try-catch 中:

(with-open+ [x 111
             (catch RuntimeException ex (println ex))
             (finally (println "finally!"))]
  (println "body"))

扩展到

(try
  (let*
    [x 111]
    (try (do (println "body")) (finally (. x clojure.core/close))))
  (catch RuntimeException ex (println ex))
  (finally (println "finally!")))

但这仍然是一个固执己见的解决方案。