Skip to content

Instantly share code, notes, and snippets.

@paxan
Last active August 29, 2015 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paxan/80352dbb47788da8dc1e to your computer and use it in GitHub Desktop.
Save paxan/80352dbb47788da8dc1e to your computer and use it in GitHub Desktop.
try-let
```
> (try-let [x (/ 5 1) y (/ 5 2)] [x y])
[5 5/2]
> (try-let [x (/ 5 1) y (/ 5 0)] [x y])
#<ArithmeticException java.lang.ArithmeticException: Divide by zero>
```
;; ComputationFailed protocol idea was inspired by: https://brehaut.net/blog/2011/error_monads
(defprotocol ComputationFailed
(has-failed? [self]))
(extend-protocol ComputationFailed
Object (has-failed? [self] false)
Throwable (has-failed? [self] true)
nil (has-failed? [self] true))
;; inspired by with-open macro
(defmacro try-let
"bindings => [name init ...]
Evaluates body only if ALL the values of the inits were computed
successfully. Returns body, or the first failed-to-init value.
Uses ComputationFailed protocol to decide if an init has failed. Out
of the box, it treats throwables raised by init expressions as
computation failures. Same goes for nils."
[bindings & body]
(when-not (vector? bindings)
(throw (IllegalArgumentException. "bindings must be a vector")))
(when-not (even? (count bindings))
(throw (IllegalArgumentException. "bindings must contain even number of forms")))
(if (zero? (count bindings))
`(do ~@body)
`(let [x# (try ~(bindings 1) (catch Throwable e# e#))]
(if (has-failed? x#)
x#
(let [~(bindings 0) x#]
(try-let ~(subvec bindings 2) ~@body))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment