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