Created
May 25, 2011 13:31
-
-
Save laurentpetit/990975 to your computer and use it in GitHub Desktop.
Apply let bindings 'til reaching the body, unless an intermediate value ...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=> (def ^{:private true :macro true} assert-args #'clojure.core/assert-args) | |
#'user/assert-args | |
=> (defmacro | |
let-unless | |
"Apply the bindings and then the body like clojure.core/let would do, unless error-fn, | |
applied to any intermediate result, returns logical true, | |
in which case no more binding will be evaluated, and the return value will be the result | |
of applying handler-fn to the result of error-fn" | |
{:arglists "([error-fn handler-fn bindings & body])"} | |
[error-fn handler-fn bindings & body] | |
(assert-args let-unless | |
(vector? bindings) "a vector for its binding") | |
(assert-args let-unless | |
(even? (count bindings)) "an even number of forms in binding vector") | |
(if-not (seq bindings) | |
`(do ~@body) | |
(let [[binding-form expr & more] bindings] | |
`(let [error-fn# ~error-fn | |
handler-fn# ~handler-fn | |
value# ~expr | |
~binding-form value#] | |
(if (error-fn# value#) | |
(handler-fn# value#) | |
(let-unless error-fn# handler-fn# ~(vec more) ~@body)))))) | |
#'user/let-unless | |
=> ;; simulating the maybe monad | |
=> (let-unless nil? (constantly "no intermediate result can be nil!") [r1 :foo r2 (println "one") r3 (println "two")] :success) | |
one | |
"no intermediate result can be nil!" | |
=> ;; usage of custom error results (of type MyError) | |
=> (defrecord MyError [msg exc]) | |
user.MyError | |
=> (defn error [x] (when (= (type x) MyError) x)) | |
#'user/error | |
=> (defn handler [err] (str "no intermediate result can be of type MyError:" err)) | |
#'user/handler | |
=> (let-unless error handler [r1 nil _ (do (println "one") (MyError. "Gasp!" nil)) r3 (println "foo")] :success) | |
one | |
"no intermediate result can be of type MyError:user.MyError@7594e821" | |
=> ;; error/handler functions can be inline or not | |
=> (defn handler [err] :failure) | |
#'user/handler | |
=> (let-unless error handler [r1 nil _ (MyError. "Gasp!" nil) r3 :step-3] :success) | |
:failure | |
=> ;; the macroexpansion looks like: | |
=> (pprint (macroexpand '(let-unless error handler [r1 nil _ (MyError. "Gasp!" nil) r3 :step-3] :success))) | |
nil | |
(let* | |
[error-fn__794__auto__ | |
error | |
handler-fn__795__auto__ | |
handler | |
value__796__auto__ | |
nil | |
r1 | |
value__796__auto__] | |
(if | |
(error-fn__794__auto__ value__796__auto__) | |
(handler-fn__795__auto__ value__796__auto__) | |
(user/let-unless | |
error-fn__794__auto__ | |
handler-fn__795__auto__ | |
[_ (MyError. "Gasp!" nil) r3 :step-3] | |
:success))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment