Skip to content

Instantly share code, notes, and snippets.

@ataggart
Last active February 13, 2016 20:06
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ataggart/377278 to your computer and use it in GitHub Desktop.
Save ataggart/377278 to your computer and use it in GitHub Desktop.
(defmacro try-let
"Behaves like try except the symbols in bindings are available to the catch
and finally clauses. The bindings' values are evaluated sequentially. If an
exception is thrown while evaluating a binding value, it and every subsequent
binding value will be nil. The catch clauses handle exceptions thrown by the
body of the try and by the evaluation of binding values.
Example:
(try-let [conn (API/openConnection)]
(do-stuff conn)
(finally (if conn (close conn)))"
{:arglists '([[bindings*] try-expr* catch-clause* finally-clause?])}
[bindings & exprs]
(let [ts (gensym)
names# (take-nth 2 bindings)
valex# (take-nth 2 (drop 1 bindings))
[body# cf#] (split-with #(not (#{'catch 'finally} (first %))) exprs)]
`(let [~ts nil
~@(interleave names# (repeat nil))
~@(interleave
(map vector names# (repeat ts))
(for [v valex#]
`(if ~ts [nil ~ts]
(try [~v nil]
(catch Throwable t# [nil t#])))))]
(try
(if ~ts
(throw ~ts)
(do ~@body#))
~@cf#))))
(comment
; turns this:
(try-let [a (somefn x) b (somefn y) c (somefn z)]
(do-stuff a b c)
(catch MyException e (println a b c)))
; into something like this (where t is a gensym):
(let [t nil ; holds the exception thrown when evaluating binding values
a nil
b nil
c nil
[a t] (if t [nil t] (try [(somefn x) nil] (catch Throwable t [nil t])))
[b t] (if t [nil t] (try [(somefn y) nil] (catch Throwable t [nil t])))
[c t] (if t [nil t] (try [(somefn z) nil] (catch Throwable t [nil t])))]
(try
(if t
(throw t)
(dostuff a b c))
(catch MyException e (println a b c))))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment