Clojure中的Try-with-resources

9

Clojure有没有类似于Java的try-with-resources结构的相应功能?

如果没有,Clojure代码中处理这种惯用语的常规方法是什么?

在Java 7之前,安全地打开和关闭资源的惯用语法已经足够冗长,以至于他们实际上为语言添加了对try-with-resources的支持。我觉得在标准Clojure库中找不到这个用例的宏很奇怪。

一个主流Clojure项目存储库的示例,展示了实践中如何处理这个问题,将非常有帮助。

2个回答

16
你可以使用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 块被创建,并且使用 tryfinally 来调用 .close() 方法。

好的,这很有道理。我仍然需要在 with-open 周围添加另一个 try 形式来捕获打开或使用资源时抛出的任何异常 - 但我想将自动打开/关闭处理与 try/catch 逻辑分开处理也不是什么坏事。我只是期望 Java 构造有一个1:1的等价物 - 但实际上没有任何理由让我期望那样。 - DaoWen
(try ...) 块中,您可以同时使用 (catch Exception e ...)(finally (.close r)),但 with-resource 宏默认不支持它,因此您需要自己编写代码。 - erdos
@DaoWen 这是一种1:1的等价关系。try (Foo x = bar()) {...}在调用bar()时不处理任何遇到的异常。它怎么可能呢?没有有效的x在作用域内,可以调用.close() - amalloy
@amalloy - 我的意思是我需要try表单和with-open,而在Java中它们都是一个结构。try-with-resources结构确实会捕获资源规范子句中抛出的异常,但你是对的,在你的例子中它不会尝试调用xclose()(因为在x初始化之前就失败了)。还有一个更微妙的语义差异:当使用Java的try-with-resources时,您将自动获得“抑制”异常的簿记。 - DaoWen

1

您可以更接近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!")))

但仍然是一种很主观的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接