Closed primo-ppcg closed 1 year ago
I've noticed this before and ultimately settled on "it doesn't matter".
Pretend you have 50 if-let bindings in a row.
The false
path has no way of knowing where in the chain the failure occurred.
Which means that depending on any of the bindings being available is inherently error-prone, which is typically the converse of the intention.
i.e the reasoning is to have the entire set of if-let bindings be pseudo-atomic, if any of them fail, they all can be considered to have failed, and the false branch is essentially error propagation.
This is in part because you usually don't assign "true", in such bindings, but rather (operation-that-could-fail)
.
There could be space for a combined macro ala (let-iflet [always bindings] [if-let bindings] :t :f)
which just translates into (let [always bindings] (if-let [if-let bindings] :t :f))
, but yeah, I ultimately don't see much benefit out of making the symbols visible since you can never depend on any of them in a way that matters (besides littering the false path with a bunch of if
s or when
s, which you could have done to begin with instead of the if-let
.
Reasonable, and certainly not difficult to work around; let if-let
as you mention is fine.
However, I do still think that the macro should be reworked, especially considering that it is the basis implementation for many others (when-with
, if-with
, when-let
, and used in about 10 others).
I ultimately don't see much benefit out of making the symbols visible since you can never depend on any of them in a way that matters
If doing so would slow the implementation or make it overly complicated, I would absolutely agree with you. It does the opposite.
Yes, def did not used to support destructuring - that looks like a reasonable simplification to me.
For future reference, this issue is incompatible with #1191, and was chosen as the victim.
I've found that forms such as this:
are invalid, as the binding of
a
is out of scope in the false branch. To paraphrase a community example:I understand that the bindings are short-circuiting, which is good and correct, but at very least the first binding should be in scope, right?
I would expect the initial
if-let
above to expand to something like the following:but instead, it expands as (gensyms renamed for brevity):
I was able to trace the current implemenation all the way back to the Janet rename on Sep 6, 2018. I suspect the implementation was necessary at the time (perhaps
def
didn't support destructuring?), but at present, the following appears to be sufficient:At very least, it passes all tests, although coverage does not appear to be complete: https://github.com/janet-lang/janet/blob/master/test/suite-boot.janet#L125-L129
But as far as I have been able to determine, there is no logical difference between this and the current implementation for any sort or number of bindings, except that it resolves the title issue. It's also significantly faster (~40% for two var bindings).
As perhaps an interesting point of discussion, the following is true for the current master:
One might debate if this is correct behavior.