Skip to content

Instantly share code, notes, and snippets.

@jtmarmon
Last active August 29, 2015 14:22
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 jtmarmon/5980c80ea98cc8cc2b59 to your computer and use it in GitHub Desktop.
Save jtmarmon/5980c80ea98cc8cc2b59 to your computer and use it in GitHub Desktop.
if-let-all-each
(defmacro if-let-all-each
"Like if-let-all but allows for a return case in each binding & only has 1 body."
[bindings & body]
(let [pairs (partition 3 bindings)
names (mapv first pairs)
exprs (map second pairs)
fail-vals (map #(nth % 2) pairs)
exprs-in-if-let (fn self [[name1 & more-names] [expr1 & more-exprs] [val1 & more-vals]]
`(if-let [~name1 ~expr1]
~(if more-names
(self more-names more-exprs more-vals))
~val1))]
(let [v (exprs-in-if-let names exprs fail-vals)]
`(if (= ~v nil)
(do ~@body)
~v))))
;; Returns the failing clause's 3rd item (e.g. if thing1 is nil, returns "thing 1 was missing")
(if-let-all-each [thing1 (d/pull db '[*] id1) "thing 1 was missing"
thing2 (d/pull db '[*] id2) "thing 2 was missing"]
;; Now I can execute this body knowing thing1 and thing2 are both here
)
;; Also tried:
(if ~more-names
~(self more-names more-exprs more-vals)
(do ~@body))
;; But that causes a stack overflow since ~(self ...) gets evaluated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment