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

Reply via email to