Skip to content

Instantly share code, notes, and snippets.

@ataggart
Last active February 13, 2016 20:06
Show Gist options
  • Save ataggart/377278 to your computer and use it in GitHub Desktop.
Save ataggart/377278 to your computer and use it in GitHub Desktop.
; Released under the Apache License, Version 2.0
; http://www.apache.org/licenses/LICENSE-2.0.html
(defmacro try-let
"A combination of try and let such that exceptions thrown in the binding or
body can be handled by catch clauses in the body, and all bindings are
available to the catch and finally clauses. If an exception is thrown while
evaluating a binding value, it and all subsequent binding values will be nil.
Example:
(try-let [x (f a)
y (f b)]
(g x y)
(catch Exception e (println a b x y e)))"
{:arglists '([[bindings*] exprs* catch-clauses* finally-clause?])}
[bindings & exprs]
(when-not (even? (count bindings))
(throw (IllegalArgumentException. "try-let requires an even number of forms in binding vector")))
(let [names (take-nth 2 bindings)
values (take-nth 2 (next bindings))
ex (gensym "ex__")]
`(let [~ex nil
~@(interleave names (repeat nil))
~@(interleave
(map vector names (repeat ex))
(for [v values]
`(if ~ex
[nil ~ex]
(try [~v nil]
(catch Throwable ~ex [nil ~ex])))))]
(try
(when ~ex (throw ~ex))
~@exprs))))
(comment
; turns this:
(try-let [x (f a)
y (f b)]
(g x y)
(catch Exception e (println a b x y e)))
; into something like this (where ex is a gensym):
(let [ex nil ; holds the exception thrown when evaluating binding values
x nil
y nil
[x ex] (if ex [nil ex] (try [(f a) nil] (catch Throwable ex [nil ex])))
[y ex] (if ex [nil ex] (try [(f b) nil] (catch Throwable ex [nil ex])))]
(try
(when ex (throw ex))
(g x y)
(catch Exception e (println a b x y e))))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment