On Mon, Jan 24, 2011 at 7:32 PM, Laurent PETIT <laurent.pe...@gmail.com> wrote: > 2011/1/25 Ken Wesson <kwess...@gmail.com>: >> Are we generally agreed that we want to preserve the body exception, >> if any, else the close exception, if any, else the body return value? >> >> Then, without further ado: >> >> (defmacro with-open2 [binding-exprs & body] >> (if (> (count binding-exprs) 2) >> `(with-open2 ~(vec (take 2 binding-exprs)) >> (with-open2 ~(vec (drop 2 binding-exprs)) >> ~@body)) >> (let [[sym expr] binding-exprs] >> `(let [~sym ~expr >> res# (try >> [(do ~@body)] >> (catch Throwable t# t#))] >> (if (vector? res#) ; body didn't throw >> (do >> (.close ~sym) ; so let close throw >> (res# 0)) >> (do ; body threw >> (try (.close ~sym) (catch Throwable _#)) ; eat close exception >> (throw res#))))))) ; and rethrow body exception >> >> user=> (with-open [x (Foo.)] (doit x)) >> #<CompilerException java.lang.Exception: close exception >> (NO_SOURCE_FILE:242)> >> user=> (with-open2 [x (Foo.)] (doit x)) >> #<CompilerException java.lang.Exception: doit exception (NO_SOURCE_FILE:40)> > > What about starting from the current definition of with-open in > clojure.core, improving upon it (and at the same time, not building > intermediate wrapper vector) : > > (in-ns 'clojure.core) > (defmacro with-open > "bindings => [name init ...] > > Evaluates body in a try expression with names bound to the values > of the inits, and a finally clause that calls (.close name) on each > name in reverse order." > {:added "1.0"} > [bindings & body] > (assert-args with-open > (vector? bindings) "a vector for its binding" > (even? (count bindings)) "an even number of forms in binding vector") > (cond > (= (count bindings) 0) `(do ~@body) > (symbol? (bindings 0)) `(let ~(subvec bindings 0 2) > (try > (let [res# (with-open ~(subvec > bindings 2) ~@body)] > ;body didn't throw > (. ~(bindings 0) close) > res#) > (catch Throwable t# > (try (. ~(bindings 0) close) (catch > Throwable _)) ; eat close exception > (throw t#)))) ; and rethrow body exception > :else (throw (IllegalArgumentException. > "with-open only allows Symbols in bindings"))))
Ah, I guess it may be a bit cleaner to just eschew "finally" altogether and close in the try and in the catch. The one thing that's a bit icky about that is that if the body close throws, the catch clause tries to close the stream again. On the other hand, my version's vector wrap is a bit icky, perhaps. :) Yours works if the binding vector is empty, too; I suppose that's a good idea. You've also incorporated the core version's error reporting code. If this makes it into core in any form it should probably include those features. :) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en