Closed tabatkins closed 6 months ago
I don't think we should conflate additional meanings onto var/const/let; as
works well.
How would this work for negative zero? Currently, the pattern -0
matches only -0, +0
matches only +0, and 0
matches both, which is quite convenient.
We'd lose the tri-state unless we special-cased it. Without a special case, we either keep the current semantics that'll recognize -0
and +0
as different (and 0
only matches +0), or we switch down to ===
semantics (so all of them are equivalent and recognize either zero).
In either case you could recover the lost behavior with functions if we accept #282, and combined with this issue they're easily invoked like when(posZero)
or when(eitherZero)
, for the rare cases when the distinction is relevant.
I would be pretty opposed to losing that tri-state zero comparison. Functions like Object.is
certainly exist to handle it, but that's much less ergonomic and robust.
(Fwiw, I think the more reasonable situation would be to drop down to ===; the default pattern should match either zero, which the current tri-state achieves, and we should maintain that.)
I'm personally okay with maintaining the special case for +0
and -0
literals, fwiw.
(This is part of several issues stemming from my discussion with Yulia.)
Currently, the "plain ident" part of the matcher syntax space is taken by irrefutable matchers (except for a small carve-out for some literal constants). That is, in
when([null, a])
, the item is in the array matcher is a literal matcher fornull
, and the second always succeeds and binds the value toa
.This syntax is pretty nice because it means that, for many simple patterns, we look identical (and act nearly identical) to destructuring -
[a, b]
binds the first two values out of an array toa
andb
in both contexts. (Patterns just also do a length-check, which destructuring doesn't.)However, in more complex cases it's not quite as obvious. When binding a value and doing further matching, the current spec uses
and
, like[a and ("foo" or "bar")]
to pass the value to both the irrefutable matcher (to produce a binding) and the other matcher. While I find this pretty reasonable, multiple people have expressed confusion about this syntax pattern.As well, this is very valuable syntax real estate. Yulia wants to be able to invoke simple function-based custom matchers (see #282) with as little syntax weight as possible, but right now you have to use the
${}
interpolation matcher. Ron wants to be able to use this space for their Extractors proposal; it's not technically clashing (Extractor patterns require a trailing()
or{}
) but it's weird to occupy the same space.Finally, several people have expressed misgivings with the current proposal's carve-out of a few literal values -
Infinity
andundefined
are both technically valid bindings that you are allowed to assign to, but we recognize them as literal matchers instead of irrefutable matchers.I think we can address all of the above by moving irrefutable matchers to a slightly different syntax. Specifically, I propose:
as <ident>
. Another possibility islet <ident>
(andconst <ident>
alongside, possiblyvar <ident>
as well).${foo}
andfoo
are exactly identical.-2
isn't actually a number, it's an unary-minus expression on the number2
); it lets authors compare against the negative of a variable without having to write${-foo}
; and it lets us at least doOption.Some
or similar.Avoiding more expression syntaxes both keeps us away from grammar issues, and leaves us more breathing room for additional patterns, like Extractor Patterns or similar using
Foo()
.