The jar is available at https://clojars.org/egamble/let-else.
let? macro has the same behavior as
let, except where a binding is followed by one or more of the keyword clauses described below, in any order.
I often find myself writing a series of
where some or all of the bindings have assertions associated with them that
would stop further binding if the assertions failed. Conceptually, that pattern
really feels like it should be a single
let form, but in practice it
has to be implemented with a bunch of nested
let? allows me to write code for that pattern as the single
let that it wants to be.
A contrived example:
(when-let [a foo] (let [b bar] (when (even? b) (let [c baz] (when (> b c) (let [d qux] (f a b c d)))))))
(let? [a foo :else nil b bar :is even? c baz :when (> b c) d qux] (f a b c d))
The rest of the bindings and the body are not evaluated if when evaluates to falsey,
in which case
let? returns the value of else if
:else else is present, otherwise nil.
E.g., these expressions with and without
let? are equivalent:
(let? [d 5 :when (> d 0) :else "error" n 3] (/ n d)) (let [d 5] (if (> d 0) (let [n 3] (/ n d)) "error"))
:is pred and
The rest of the bindings and the body are not evaluated if pred applied to the value
of the binding expression is falsey (for
:is) or truthy (for
:is-not). Works even with
:is-not can all be present on the same binding.
E.g., these three expressions are equivalent:
(let? [a "foo" :is not-empty :else "error" b "bar"] (str a b)) (let? [a "foo" :is-not empty? :else "error" b "bar"] (str a b)) (let [a "foo"] (if (not-empty a) (let [b "bar"] (str a b)) "error"))
The value of else is the value of the
let? if the associated
to falsey, or
:is-not evaluates to truthy.
:else without a
:is-not, if the value of the binding expression
is falsey, else is the value of the
E.g., these expressions are equivalent:
(let? [a (foo) :else nil b (bar) :else "error"] [a b]) (when-let [a (foo)] (if-let [b (bar)] [a b] "error"))
:is-not clauses, the else is evaluated inside the context of
the binding, otherwise outside, e.g.
(let [a 4] (let? [a 3 :is even? :else (str a " is not even")] nil)) => "3 is not even" (let [a 4] (let? [a nil :else a] nil)) => 4
:delay clause following a binding of a symbol (not a destructuring form) delays
evaluation of the binding value until it is actually used, in case there is a possibility
it won't be used at all.
:delay is ignored if the binding has any other keyword clauses.
:delay may be specified as metadata preceding the symbol, e.g.
(let? [^:delay x (foo)] ...)
is equivalent to:
(let? [x (foo) :delay true] ...)
The keyword clause
:else nil with no other keyword clauses is equivalent to
Some people find
:else nil awkward, since nil is already the value of
the binding value is falsey. An equivalent keyword clause is
let-else/truthy is defined as a synonym of
A not-quite-equivalent keyword clause is
:is-not nil?, which distinguishes between nil
and not-nil rather than falsey and truthy.
Added unit tests. Fixed the behavior of
:when nil which was being ignored. Updated Clojure dependency to 1.6.0.
Removed dependency on flatland/useful and updated Clojure dependency to 1.5.1.
Updated Clojure dependency to 1.5.0.
truthy as a synonym of
identity, for use in the clause
- Added the new keyword clause
- Changed the behavior of
:elseelse in the presence of
:is-notso that else is evaluated inside the context of the binding.
:elsewithout other keyword clauses is still evaluated outside the binding context.
:delayis now ignored when other keyword clauses are present.
Fixed the behavior of
:else falsey which was incorrectly being ignored.
Added the new keyword clause