Last active
June 7, 2021 11:49
-
-
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
This file contains hidden or 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
(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