Why don't when-let
and if-let
support multiple bindings by default?
So:
(when-let [a ... b ...] (+ a b))
...instead of:
(when-let [a ... (when-let [b ... (+ a b)))
I am aware that I can write my own macro or use a monad (as described here: http://inclojurewetrust.blogspot.com/2010/12/when-let-maybe.html).
Because (for if-let
, at least) it's not obvious what to do with the "else" cases.
At least, motivated by Better way to nest if-let in clojure I started to write a macro that did this. Given
(if-let* [a ... b ...] action other)
it would generate
(if-let [a ...] (if-let [b ...] action ?))
and it wasn't clear to me how to continue (there are two places for "else").
You can say that there should be a single alternative for any failure, or none for when-let
, but if any of the tests mutate state then things are still going to get messy.
In short, it's a little more complicated than I expected, and so I guess the current approach avoids having to make a call on what the solution should be.
Another way of saying the same thing: you're assuming if-let
should nest like let
. A better model might be cond
, which isn't a "nested if" but more an "alternative if", and so doesn't fit well with scopes... or, yet another way of saying it: if
doesn't handle this case any better.
Here is when-let*:
(defmacro when-let* "Multiple binding version of when-let" [bindings & body] (if (seq bindings) `(when-let [~(first bindings) ~(second bindings)] (when-let* ~(vec (drop 2 bindings)) ~@body)) `(do ~@body)))
Usage:
user=> (when-let* [a 1 b 2 c 3] (println "yeah!") a) ;;=>yeah! ;;=>1 user=> (when-let* [a 1 b nil c 3] (println "damn! b is nil") a) ;;=>nil
Here is if-let*:
(defmacro if-let* "Multiple binding version of if-let" ([bindings then] `(if-let* ~bindings ~then nil)) ([bindings then else] (if (seq bindings) `(if-let [~(first bindings) ~(second bindings)] (if-let* ~(vec (drop 2 bindings)) ~then ~else) ~else) then)))
Usage:
user=> (if-let* [a 1 b 2 c (+ a b)] c :some-val) ;;=> 3 user=> (if-let* [a 1 b "Damn!" c nil] a :some-val) ;;=> :some-val
EDIT: It turned out bindings should not be leaked in the else form.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With