There it is, corrected: (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) (let [res# (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) res#)) :else (throw (IllegalArgumentException. "with-open only allows Symbols in bindings")))) 2011/1/25 Laurent PETIT <laurent.pe...@gmail.com>: > of course I missed something :-( > > 2011/1/25 Laurent PETIT <laurent.pe...@gmail.com>: >> 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