Skip to content

Instantly share code, notes, and snippets.

@kendru
Last active March 15, 2020 22:59
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 kendru/71473e07e7b54e050c9d6485ebe491b7 to your computer and use it in GitHub Desktop.
Save kendru/71473e07e7b54e050c9d6485ebe491b7 to your computer and use it in GitHub Desktop.
Clojure macro for taking values from a number of channels, short-circuiting on an error value
(defn is-error-clause? [expr]
(and (sequential? expr)
(= 'on-error (first expr))))
(defn parse-clauses [exprs]
(let [[body error-clauses] (partition-by is-error-clause? exprs)
[_ error-name & error-body] (first error-clauses)]
{:body body
:error-name error-name
:error-body error-body}))
(defmacro <all
"Given a set of bindings where the righthand evaluates to a channel that
may succeed with `[:ok val]` or fail with `[:error err]`, run the first body
clause with all bindings bound to the success values. A second clause may be
given with a var and a body, in which case any error value will be handled by
this clause, and the error value will be bound to the var given.
Must be called from inside a go block.
Example:
(go
(<all [v1 chan1
v2 (get-chan)
v3 chan3]
(println v1)
(println v2)
(println v3)
(on-error err
(println \"Error:\" err))))"
[bindings & exprs]
(let [{:keys [body error-name error-body]} (parse-clauses exprs)
val-sym (gensym)]
(if (seq bindings)
(let [[name expr & next] bindings]
`(let [[status# ~val-sym] (cljs.core.async/<! ~expr)]
(if (= status# :ok)
(let [~name ~val-sym]
(<all ~next ~@exprs))
~(if error-name
`(let [~error-name ~val-sym] (do ~@error-body))
'nil))))
`(do ~@body))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment