On Mon, Jan 24, 2011 at 7:43 PM, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> 2011/1/25 Ken Wesson <kwess...@gmail.com>:
>> 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.
>
> argh yes, this may be a problem ...

There's also a bug: _ instead of _# in the close exception discarder. :)

How's about:

(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 _#))
                                             (throw t#)))]
                                (. ~(bindings 0) close)
                                res#))
   :else (throw (IllegalArgumentException.
                  "with-open only allows Symbols in bindings"))))

No double-closing and no vector. If the body throws, the exception is
caught, the close is done with any close exception suppressed, and the
original exception rethrown and that's it. If the body doesn't throw,
we store it in res and then close. If the close throws here, the
exception is allowed to propagate. Otherwise we return res.

I've tested this with Foo and it works; modifying Foo to replace the
close throw with (println "closed"), the doit throw with the integer
literal 42, or both, I get:

Both throw:
#<CompilerException java.lang.Exception: doit exception (NO_SOURCE_FILE:46)>

Close throws:
#<CompilerException java.lang.Exception: close exception (NO_SOURCE_FILE:48)>

Doit throws:
closed
#<CompilerException java.lang.Exception: doit exception (NO_SOURCE_FILE:46)>

Neither throws:
closed
42

The right exception escapes if any exceptions are thrown, and "closed"
is printed exactly once if close doesn't throw.

-- 
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

Reply via email to