Should array patterns/patterns expressed with square brackets only match arrays, or should they match any iterable type? I see this as potentially providing two benefits and two costs.
First, matching any iterable would more closely mirror the behavior of the square brackets in the existing destructuring syntax. Second, this would allow the writing of extractors which support nested pattern matching using both positional and named subpatterns. An extractor which provides the same values as both normal object fields and as positional arguments accessible via an iterator could thus have its contents matched and bound by a nested object pattern or by a nested array pattern, depending on whether the author of the pattern valued explicitness or concision in a given case.
The cost of this is that additional effort would be needed to differentiate an array from another iterable type in a match statement. I suspect that this would be a niche case, which could likely be solved by the proper ordering of patterns when it would occur. To support cases where differentiation is absolutely necessary, an extractor could be added to the Array class which performs an Array.isArray check and then returns the array unchanged. The second cost of this change would be a performance hit in pattern matching in cases where the additional iterable machinery became necessary.
(The "closing and then reopening this" thing was because I accidentally submitted this issue before I was done typing it)
This was something that came up while I was talking to folks about array destructuring: _right now_, it operates on Array-likes, not necessarily Arrays, but it definitely should be based on iterators, as the destructuringevaluation works. I guess this is as good a reason as any to go ahead and make that change to the spec and pattycake.
It makes sense to based it on iterators, but the use case for matching strictly on an array is pretty common - far more common than matching on iterators, I’d think - and being forced to add an isArray guard is pretty unergonomic.
@ljharb that's what extractors are intended for. But that's a "later" thing.
It definitely makes sense to defer extractors; but i think that Array matching - way more than iterator matching - will be desired, enough that “we’ll handle it later with an extractor” might not suffice.
It matches arrays just fine, just like destructuring binding currently does. Without having to pass in the iterator yourself.
Sorry, to clarify: matching an array but rejecting a non-array iterable.
I think we should actively discourage language features that discriminate between arrays and non-array iterables. That has been the trend since ES2015 and should continue.
Yeah no, rejecting non-array iterables doesn't make sense to me and seems kinda random tbh.
Specifically because a string is iterable; there's tons of cases where I'd want to reject strings but accept arrays (even if i accepted all other iterables). However, I suppose i could use a guard to only match when the type wasn't a string, or when it was an array - but I think the fact that strings are default iterable continues to be a footgun - it's come up on other proposals in the past (flatMap, as I recall).
About iterable strings - we already have a precedent in Typed Array constructors - they work with iterable objects, but primitives perceived as another case.
Ugh, the "strings are iterable" thing is still so so terrible. It would be super annoying if the following failed:
const x = match("foo") {
[a, ...b] => console.log("found an array, OR A STRING WHOOPS");
"foo" => console.log("won't hit this :(((((");
};
It would be somewhat inconsistent, but it would be really cool if we could somehow start shifting the language over to not treating strings as iterables by default; you just need to spread the string into an array, or get its iterator manually, to grab it as letters.
The "strings are iterable" thing is -awesome-:
match ('~zkat') {
['~', ...username] => username.join('')
}
I'm totally onboard with keeping this behavior, and I think preserving consistency with existing destructuring is CRITICAL for this particular spec. I'm ok with the gotcha you're talking about, specially since I'm willing to bank on this sort of multi-type matches being more rare than not. The vast majority of the will be against similar data structures (always objects, always arrays, always strings, always numbers, etc).
I'm gonna close this: I think you'd need some exceptionally strong arguments to somehow change the way destructuring binding works in variable assignment, and I think it's a very important core feature of this proposal that its semantics be 1:1 to JS' destructuring syntax -- warts and all. At least you only have to learn the wart once. As stated previously, I don't think this is going to be that much of a hazard in practice, and I still think the right time to address that would've been when speccing out destructuring. That ship has sailed, and we're all left sobbing at the docks.
Most helpful comment
Ugh, the "strings are iterable" thing is still so so terrible. It would be super annoying if the following failed:
It would be somewhat inconsistent, but it would be really cool if we could somehow start shifting the language over to not treating strings as iterables by default; you just need to spread the string into an array, or get its iterator manually, to grab it as letters.