2011/1/25 Ken Wesson <[email protected]>:
> On Mon, Jan 24, 2011 at 7:32 PM, Laurent PETIT <[email protected]>
> wrote:
>> 2011/1/25 Ken Wesson <[email protected]>:
>>> 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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en