Description of issue:
Suppose I want to create multiple bindings with let and then execute a body. I can do that easily like so:
(let [a 1
b (inc a)
c (* b b)]
[a b c])
But, if I want to do the same type of thing with if-let, I can only do so by nesting them because if-let only accepts one binding at a time.
(if-let [a 1]
(if-let [b (inc a)]
(if-let [c (* b b)]
[a b c]
"error")
"error")
"error")
This is very inelegant because:
1) It is not as simple to read as it would be if all of the bindings were next to each other on the same indentation
2) The else clause it duplicated multiple times.
3) The else clause is evaluated in a different context depending which binding failed. What if a was already bound to something? If the if-let shadows a, and b does not get bound, the else clause would be executed with a different value bound to a than if a was not shadowed in the first if-let. (see code below for example)
I want to be able to write this instead:
(if-let [a 1
b (inc a)
c (* b b)]
[a b c]
"error")
=> [1 2 4]
(let [a :original]
(if-let [a :shadowed
b false]
a a))
=> :original
I also want to be able to do a similar thing with when-let, if-some, and when-some.
*Proposed:*
I re-wrote those macros to be able to handle multiple bindings. If supplied with just one binding, their behavior remains identical. If supplied with multiple bindings, they should only execute the body if every binding passed. In the case of some bindings passing and some failing in if-let or if-some, none of the bindings should leak into the else clause.
*Patches:*
- clojure-core v2 8-3-2017.patch - Clojure patch with macro updates. For if-let and if-some, I had to add a bit of extra logic in order to prevent them from leaking bindings to the else clause in the case of some bindings passing and some failing. It also includes a few extra tests around each macro.
- core.specs.alpha.patch - core.specs.alpha patch with equivalent updates to core specs