Skip to content

Instantly share code, notes, and snippets.

@mitranim
Last active June 7, 2021 11:49
Show Gist options
  • Save mitranim/148f0a0b51f36ef6a2cd3a0996dc7104 to your computer and use it in GitHub Desktop.
Save mitranim/148f0a0b51f36ef6a2cd3a0996dc7104 to your computer and use it in GitHub Desktop.
Clojure macros for writing flat blocks with early returns, avoiding the let/if-pyramid
(defmacro do?
"Variant of a 'do' block with early interruption, in the style of
Haskell's 'do' notation. Supports the popular Clojure pattern
of '{:error _}' vs result.
When a subform evaluates to '{:error _}', the block short-curcuits,
immediately returning this value.
See also: 'err?', 'to-err'.
Expansion:
(do?
expr | (let [value expr] (if (err? value) value
expr | (let [value expr] (if (err? value) value
expr | (let [value expr] (if (err? value) value)))))))
"
[& exprs]
(when-let [[expr & tail-exprs] (seq exprs)]
(if-not tail-exprs
expr
`(let [value# ~expr]
(if (err? value#) value# (do? ~@tail-exprs))))))
(defn expr-imp? [[expr & tail-exprs]]
(if-not tail-exprs
expr
(match expr
(('let pattern init) :seq)
`(let [value# ~init]
(if (err? value#) value# (let [~pattern value#] ~(expr-imp? tail-exprs))))
(('let & _) :seq)
(throw (new IllegalArgumentException
"in 'imp' root, 'let' must have the form: (let pattern init)"))
:else
`(let [value# ~expr]
(if (err? value#) value# ~(expr-imp? tail-exprs))))))
(defmacro imp?
"Variant of a 'do' block that emulates imperative assignment and early
interruption. Based on 'imp' and 'do?'. See '(doc imp)' for assignment,
and '(doc do?)' for interruption.
Usage:
(imp?
(let pattern error-or-result)
(let pattern error-or-result)
error-or-result
error-or-result
(let pattern error-or-result)
error-or-result
...)
Expansion:
(imp?
(let pattern init) | (let [result init] (if (err? result) result (let [pattern result]
(let pattern init) | (let [result init] (if (err? result) result (let [pattern result]
expr | (let [result expr] (if (err? result) result
(let pattern init) | (let [result init] (if (err? result) result (let [pattern result]
expr | (let [result expr] (if (err? result) result
expr | (let [result expr] (if (err? result) result
(let pattern init) | (let [result init] (if (err? result) result (let [pattern result]
(let pattern init) | (let [result init] (if (err? result) result (let [pattern result]
expr | (let [result expr] (if (err? result) result
expr | (let [result expr] (if (err? result) result)))))))))))))))))))))))))
"
[& exprs]
(expr-imp? exprs))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment