2011/1/25 Ken Wesson <kwess...@gmail.com>: > 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. :)
What about this version which gets rid of the double call to close ? Did I miss something (it's getting late here) ? (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 (with-open ~(subvec bindings 2) ~@body) (catch Throwable t# (try (. ~(bindings 0) close) (catch Throwable _)) ; eat close exception (throw t#))) ; and rethrow body exception (. ~(bindings 0) close)) :else (throw (IllegalArgumentException. "with-open only allows Symbols in bindings")))) -- 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