2023-10-02 [07:42:03.0157] I'll be a few minutes late to the meeting this morning [08:49:53.0499] Notes from today's meeting: https://github.com/tc39/proposal-pattern-matching/issues/302 [13:34:48.0562] Opened a new issue about Matthew's perf concern about extractor vs variable custom matchers: https://github.com/tc39/proposal-pattern-matching/issues/303 2023-10-03 [19:01:56.0681] > <@rkirsling:matrix.org> in theory you could keep nesting `is ... if (...)`s I think this point may have been overlooked in the discussion about `is` and `if` [19:03:39.0464] this really is something that arises from the combination of the two [19:06:49.0600] though I guess it's not exclusive to `if` _patterns_; it's mostly something that happens due to `is` existing [19:07:50.0334] though as Ron mentioned immediately above, the spelling of `if` pattterns/guards as `if` is the compounding factors [19:07:51.0600] * though as Ron mentioned immediately above, the spelling of `if` pattterns/guards as `if` is the compounding factor [19:08:10.0285] * I guess it's not exclusive to `if` _patterns_; it's mostly something that happens due to `is` existing, but [19:08:13.0755] * as Ron mentioned immediately above, the spelling of `if` pattterns/guards as `if` is the compounding factor [19:11:03.0512] basically ``` if (... is ... if (... is ... if (...))) if (...) return; ``` would be our new world of JS [19:11:15.0627] * basically ``` if (... is ... if (... is ... if (...))) if (...) return; ``` would be valid in our proposed world [19:12:59.0535] syntactically valid does not mean developers must write that. otherwise `throw throw x` should also be banned [19:13:27.0416] that's not a counterargument [19:18:36.0706] it would be completely negligent to not have a thorough discussion about such a case [19:20:25.0203] the crux is that it would be extremely easy to write code in one can't easily tell what construct `if (...)` is [19:21:39.0422] * the crux is that it would be extremely easy to write code in which one can't easily tell what construct `if (...)` is [19:22:28.0208] but both if statement and if pattern accept an expression and evaluate it and do truthy test [19:22:55.0827] it's not so important to distinguish them, because they have the same semantics [19:23:50.0370] they absolutely do not have the same semantics; one is a control flow construct and one is a subexpression [19:34:57.0263] * they absolutely do not have the same semantics; one is a control flow construct and one is a boolean subexpression [01:12:08.0203] fwiw i'm strongly opposed to dropping `when` entirely [01:12:26.0876] also is matthew mathieu? [01:12:52.0119] * fwiw i'm strongly opposed to dropping `when` entirely (but am flexible on spelling) [01:15:49.0297] also i commented on the perf concern on the issue; tldr, this isn't a thing that's usually a problem and not something we should be concerned about [08:08:04.0473] it me. [08:08:10.0738] I am implementer. Raising concern. [08:08:17.0634] FWIW [08:09:07.0289] The iterator protocol is hard to optimize, and I'm trying to provide feedback that will result in a performant proposal [08:09:17.0930] or, at least have the -potential- to be performant [08:49:18.0682] I think it's fine to pay the cost for classes, but maybe we just don't support unboxing for primitives. Either that, or we distinguish between `x is String` and `x is String()` somehow. Different symbol methods maybe? [10:59:55.0143] Yeah that was my suggestion in the issue. [11:00:39.0011] I assume that authors will similarly run into issues where constructing the iterator's contents might be expensive, and want to avoid doing that when the user is just calling `Foo` for a typecheck. [11:03:36.0703] > <@rkirsling:matrix.org> the crux is that it would be extremely easy to write code in which one can't easily tell what construct `if (...)` is I think this is relevant if it's *easy* to write code like that and there is *benefit* to doing it, such that we can expect it to be a somewhat common pattern among authors. If it's just *possible* but *fairly silly* to write such code, then so long as the behavior is well-defined and not too surprising it's not something we need to worry about, imo. People can do *lots* of very silly things in their code; this is far from the only place where authors can nest to ridiculous degrees if they feel like it, and make their code hard to understand. [11:07:08.0636] For example, one can similarly nest IIAFEs into their if(), or deeply nested ternaries, both of which can be quite difficult to puzzle out. But there's rarely, if ever, a reason to do so, so we don't worry about it; the behavior is well-defined and, if you can follow the expression tree, unsurprising. [13:55:37.0998] ljharb: I'm resolving all your whitespace changes without merging them; I don't think there is remotely a common standard on this, and I have written code in this way my entire life. [14:00:18.0084] > <@ljharb:matrix.org> fwiw i'm strongly opposed to dropping `when` entirely (but am flexible on spelling) Can you elaborate on this? What is your opposition? [14:01:04.0497] And notably, what in your mind distinguishes JS from most other languages which lack a clause-introducing keyword in their syntax? [16:04:44.0245] > <@mgaudet:mozilla.org> I am implementer. Raising concern. aha thanks, that clarifies :-) [16:05:17.0573] > <@rbuckton:matrix.org> I think it's fine to pay the cost for classes, but maybe we just don't support unboxing for primitives. Either that, or we distinguish between `x is String` and `x is String()` somehow. Different symbol methods maybe? i think it’s extremely important to unbox primitives by default; it’s a mistake the language makes that so difficult already. [16:06:02.0026] > <@tabatkins:matrix.org> Can you elaborate on this? What is your opposition? i think it’s very valuable to have a clear syntactic marker for the start of a clause. (a word is clear, a token likely isn’t) [16:06:53.0673] Why do you think we need a marker beyond "start of the line"? the obvious formatting will make this very clear when read. [16:07:18.0782] It's my impression that most TC39 members believe that boxing primitives is a bad idea to begin with, so I'm not sure interacting with boxing is a benefit if it might encourage it's use even more [16:07:41.0185] > <@tabatkins:matrix.org> And notably, what in your mind distinguishes JS from most other languages which lack a clause-introducing keyword in their syntax? i care zero for other languages; i think our bar should be higher than that. Private fields, for example, avoids repeating a mistake every other language makes by making them actually private and not reflectable. [16:07:59.0719] > <@rbuckton:matrix.org> It's my impression that most TC39 members believe that boxing primitives is a bad idea to begin with, so I'm not sure interacting with boxing is a benefit if it might encourage it's use even more absolutely, but this helps *reduce* boxing [16:08:21.0923] > <@tabatkins:matrix.org> Why do you think we need a marker beyond "start of the line"? the obvious formatting will make this very clear when read. one thing JS has objectively demonstrated is that there’s no obvious formatting, ever [16:08:37.0768] > <@ljharb:matrix.org> absolutely, but this helps *reduce* boxing No it makes it easier to unbox. If that feature isn't there, then you must manually unbox, which discourages boxing. [16:09:24.0035] > <@rbuckton:matrix.org> No it makes it easier to unbox. If that feature isn't there, then you must manually unbox, which discourages boxing. that’s a false conclusion. If someone boxes a primitive it’s because they don’t know any better, not because of the ease of unboxing. Also, the value you’re testing is highly likely not to be one you control or create. [16:09:34.0903] lol I think "start clauses on a new line, indent wrapped" if extremely obvious and if you do anything else you're hurting yourself in a way that we don't need to help you avoid. [16:09:49.0462] > <@ljharb:matrix.org> i care zero for other languages; i think our bar should be higher than that. Private fields, for example, avoids repeating a mistake every other language makes by making them actually private and not reflectable. The ability to reflect over private fields in in some languages is based on an established trust relationship. Unprivileged code in .NET cannot reflect over private fields. [16:10:04.0803] > <@tabatkins:matrix.org> lol I think "start clauses on a new line, indent wrapped" if extremely obvious and if you do anything else you're hurting yourself in a way that we don't need to help you avoid. i agree with your sensibilities but not that people are rational enough in aggregate for that to hold. [16:10:37.0137] > <@ljharb:matrix.org> i care zero for other languages; i think our bar should be higher than that. Private fields, for example, avoids repeating a mistake every other language makes by making them actually private and not reflectable. I also don't agree what introducing a keyword sets a higher bar, it introduces unnecessary and avoidable repetition. [16:10:41.0131] the unboxing primitives discussion is a red herring - the point is that the built-in matchers are slightly expensive (and author-provided ones can potentially be more expensive), which is an annoying cost to pay when we're going to immediately throw away the value anyway because all we care about is "not false" [16:10:46.0267] > <@ljharb:matrix.org> i care zero for other languages; i think our bar should be higher than that. Private fields, for example, avoids repeating a mistake every other language makes by making them actually private and not reflectable. * I also don't agree that introducing a keyword sets a higher bar, it introduces unnecessary and avoidable repetition. [16:11:09.0606] I’m very open to flexibility in the custom matcher protocol return value, to be clear [16:11:16.0749] i don’t think a second protocol makes any sense at all [16:11:42.0944] what I’m not open to is unnecessarily restrictive semantics for builtin primitives [16:12:45.0894] i don’t see how an internal slot check is hard to optimize; many prototype methods check it, on strings also. [16:12:54.0475] > <@ljharb:matrix.org> i don’t think a second protocol makes any sense at all Extractors are unary functions by design. If you want to control the return value somehow, you really shouldn't be doing that via an extra argument, IMO. [16:13:04.0457] and if it’s hard for userland code to match that, all the more reason for host code to have it available [16:13:24.0415] > <@rbuckton:matrix.org> Extractors are unary functions by design. If you want to control the return value somehow, you really shouldn't be doing that via an extra argument, IMO. I’m sure there’s more nuance here we can discuss next meeting when I’m not still traveling. [16:16:21.0299] > <@ljharb:matrix.org> i don’t see how an internal slot check is hard to optimize; many prototype methods check it, on strings also. I'm confused; that's not the cost being mentioned. It's the cost of creating a temp array (and for userland code, the potentially unbounded cost of constructing what goes in the array). [16:16:52.0996] ok so that’s about the return value, which has nothing necessarily to do with the semantics. i agree that it’d be nice to avoid that [16:17:17.0502] however that same concern just came up last week wrt iterator joining, and nobody seemed worried about it [16:17:33.0652] Right. `Foo` will *always* ignore the return value other than checking if it's false or not, so incurring the cost of actually building the return value is unfortunate. Thus the pair of protocols. [16:17:35.0737] so I’m confused why mgaudet thinks a throwaway array is expensive here but fine in dozens of other places [16:17:58.0444] adding a new protocol seems way more expensive than a temp array imo [16:18:14.0112] Those are completely different types of cost; I don't see the connection. [16:18:28.0866] but since is produces bindings I’m not sure when it’s ever just a “test” [16:18:38.0558] ??? [16:19:06.0319] I'm talking about the pattern `Foo`, as opposed to the pattern `Foo(...)`. [16:19:32.0352] i think this will need to wait til we can discuss sync to clarify [16:19:37.0218] Right now they both invoke the same method, but the former throws away the return value. [16:19:49.0098] Yeah, I'm *super* confused as to what you're actually talking about, ljharb ^_^ [16:20:23.0789] the issue you filed and the notes summary cover multiple “conclusions”, some of which i strongly oppose, so we’re probably talking past each other 2023-10-04 [17:00:53.0951] > <@tabatkins:matrix.org> I think this is relevant if it's *easy* to write code like that and there is *benefit* to doing it, such that we can expect it to be a somewhat common pattern among authors. If it's just *possible* but *fairly silly* to write such code, then so long as the behavior is well-defined and not too surprising it's not something we need to worry about, imo. People can do *lots* of very silly things in their code; this is far from the only place where authors can nest to ridiculous degrees if they feel like it, and make their code hard to understand. I don't really understand how to interact with this group when the response to my concerns is to pretend they don't exist [17:02:00.0098] Tweeting "have you tried just not writing bad code?" is already not going to be a way to win friends when we're talking about _existing_ features [17:02:07.0268] I'm not doing that? I'm providing my own reasoning. [17:02:21.0337] This is a new feature; it needs to justify its own existencr [17:02:29.0734] * This is a new feature; it needs to justify its own existence [17:03:39.0152] Like, I provided significant qualifications to my statement. I think they all apply here, and justify my judgement that is okay. If you disagree, feel free to say why! Especially if you feel any of my assumptions don't hold, it perhaps don't hold as strongly as I might think they do. [17:04:24.0232] * Like, I provided significant qualifications to my statement. I think they all apply here, and justify my judgement that is okay. If you disagree, feel free to say why! Especially if you feel any of my assumptions don't hold, or perhaps don't hold as strongly as I might think they do. [17:07:06.0902] An issue is that I think your objection, as stated, is a fully general counter argument to any new feature that can be nested; you can always sufficiently nest it to make it unreadable. Thus my qualifications about when I think your (valid!) concern applies, and why I think this situation doesn't meet those criteria. [17:07:20.0681] I said it was extremely easy to write code that's very hard to read. This is literally part of my _initial reaction_. I'm not looking for edge cases, I'm literally just spelling out the basic facts of what this does to JS. [17:07:39.0416] This is all brand new to me; I am your eyes before plenary shuts you down [17:09:14.0651] Like, my expressions of reservation during plenary this week were all met with "oh, I'll be harsher if you're not willing" [17:10:06.0630] Yup, and my counter was that I don't *currently* believe that the problematic code you gave an example of is actually a realistic thing someone will write. I could be wrong; a semi-realistic example would help. [17:12:41.0567] With sufficient elision, it's similarly easy to show short code that puts an IIAFE inside the default argument value of the arglist of another IIAFE, for example, which is practically unreadable with only one level of nesting. We don't judge that to be a concern tho, because it's rare and not particularly helpful vs the more readable ways to write that code. [17:13:14.0625] But there are other examples one can raise for various features that are realistic *and* similarly unreadable, and that's a problem to address [17:13:22.0356] So the specifics matter, is what I'm saying [17:15:38.0345] Sure but I pointed out a basic fact -- `if (...)` would now be two totally different constructs, which not only can appear adjacently but the new one is also nestable -- and there is strong interest in having me not say that but no interest in being like, "oh yes, we _could_ come up with a terrible example, couldn't we" [17:18:02.0081] Seems like we're only looking for people to nod and agree [17:39:53.0195] > <@ljharb:matrix.org> i think it’s extremely important to unbox primitives by default; it’s a mistake the language makes that so difficult already. but nobody really use them right? boxed primitives [17:40:13.0815] Not on purpose, but way more than people realize by accident. [17:40:53.0990] My goal here is to absorb those accidents and be able to write code that pretends boxed primitives doesn’t exist, and *still works with them*, something that’s quite difficult today. [17:51:06.0125] > <@ljharb:matrix.org> My goal here is to absorb those accidents and be able to write code that pretends boxed primitives doesn’t exist, and *still works with them*, something that’s quite difficult today. Having `x is String` work for both boxed and unboxed primitives is fine. I'm not sure how valuable `x is String(let y)` is though. If you are writing that you are definitely *not* pretending boxed primitives don't exist. [18:06:35.0728] i agree there’s no value there. However that seems like an unavoidable thing to fall out of the proposal’s semantics, and not a harmful one since it’ll be rare (unless it slows down normal usage, ofc) [18:38:15.0577] So what's getting totally lost is why we want the things that we want. `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` certainly makes sense. Still, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. And indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. Patterns ought to be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, and ironically that's the case where `and` is not the most intuitive keyword to read. [18:40:58.0006] * So what's getting totally lost is why we want the things that we want. `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` certainly makes sense. Simultaneously, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. And indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. Patterns ought to be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, and ironically that's the case where `and` is not the most intuitive keyword to read. [18:41:17.0562] * So what's getting totally lost is why we want the things that we want. `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` makes a lot of sense. Simultaneously, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. And indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. Patterns ought to be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, and ironically that's the case where `and` is not the most intuitive keyword to read. [18:41:23.0784] * So what's getting totally lost is why we want the things that we want. `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` makes a lot of sense. Simultaneously though, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. And indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. Patterns ought to be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, and ironically that's the case where `and` is not the most intuitive keyword to read. [18:46:18.0672] * So what's getting totally lost is why we want the things that we want. `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` makes a lot of sense. Simultaneously though, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. Indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. Patterns ought to be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, and ironically that's exactly the case where `and` is not the most intuitive keyword to read. [18:50:18.0459] * So what's getting totally lost is why we want the things that we want. `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` makes a lot of sense. Simultaneously though, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. Indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. I believe strongly that patterns should be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, but ironically that's exactly the case where `and` is not the most intuitive keyword to read. [18:50:54.0782] * So what's getting totally lost is why we want the things that we want. `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` makes a lot of sense. Simultaneously though, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. Indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. I believe strongly that patterns should be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, yet ironically that's exactly the case where `and` is not the most intuitive keyword to read. [18:52:02.0620] * Like, my expressions of reservation during plenary this past week were all met with "oh, I'll be harsher if you're not willing" [18:55:47.0302] > <@rkirsling:matrix.org> So what's getting totally lost is why we want the things that we want. > > `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` makes a lot of sense. Simultaneously though, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. > > Indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. > > Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. I believe strongly that patterns should be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, yet ironically that's exactly the case where `and` is not the most intuitive keyword to read. half a dozen sounds high? I recall mentioning it in relation to the negated `in`/`instanceof` proposal, which I had mentioned here in advance of plenary. I don't recall much more than that. [18:56:39.0087] `if (let { foo } = x)` may align with existing destructuring, but existing destructuring cannot be easily extended into a full pattern matching mechanism. [18:58:03.0518] I also don't have a strong opinion on whether `if` should be excluded from `is`, only that if we have `if` patterns, then we shouldn't differ in what is allowed in the Pattern syntax between `if` and `match`. [18:58:34.0044] "half-dozen" might be rounding up 😅 but I mean it in terms of folks' perception, regardless of the precise reality [18:59:45.0988] If anything, I feel that `is not` is far more palatable than `!in` and `!instanceof`, though that comes from a position of trying not to clash with TypeScript syntax if possible. [19:00:33.0981] Also, `if (let { foo } = x)` doesn't really work if it's not actually doing pattern matching. There's no condition in that case. [19:01:23.0364] it may be the case that destructuring can't be extending into pattern matching, but what I'm saying is that we have a great language without pattern matching which wants for an if-let construct [19:01:59.0173] to introduce not destructuring but instead a "competitor" to it in that space feels like the language is attacking itself [19:02:00.0484] I believe we have a great language which wants for a pattern matching mechanism. [19:02:55.0907] There are so many things pattern matching can tie into with future proposals that can build upon it. A standalone `if let` mechanism is almost a throwaway feature. It doesn't add any other intrinsic value. [19:04:11.0823] Pattern matching is a boon for FP-style development and is a vast improvement in terms of expressivity and usability of the language, especially with a cohesive syntax. [19:05:09.0873] If anything, I see "this could be accomplished with pattern matching" to be an indication that we're on the path to getting this right. Not in a "shoehorn everything into pattern syntax" way, but in a "natural progression of the feature and its capabilities" way. [19:07:04.0041] > <@rbuckton:matrix.org> There are so many things pattern matching can tie into with future proposals that can build upon it. A standalone `if let` mechanism is almost a throwaway feature. It doesn't add any other intrinsic value. that's a very difficult philosophy for me to relate to 😓 ideally we'd never need to add anything to the language because it would already be enough. `if`-`let` is a simple extension of what we already have which adds value without introducing any worries about the ship capsizing [19:07:21.0836] If anything, I find it regrettable that destructuring was defined to be so loose in how it handles patterns, which makes it very difficult to extend into a full blown pattern matching mechanism. [19:08:00.0558] I don't see how `if let` is useful *without* pattern matching to go with it. [19:10:58.0481] I don't see JS as 100% done. It is merely "sufficient". There are far too many rough edges and improvements that can be made in the developer experience. Why add `class` when `function` and `prototype` are sufficient? Why add `let` when `var` is sufficient? [19:11:40.0303] That said, I'm not advocating for everything under the sun to be included in JS. [19:12:00.0071] I'm advocating for features that provide a tangible benefit to a sizable population of JS users. [19:13:21.0463] I advocated for class decorators to improve the developer experience for OOP devs. I have advocated for pattern matching to improve the experience for FP devs. [19:14:20.0050] I'm advocating for shared structs to improve the runtime capabilities for large applications like Office Web Apps, Google Docs, Teams, etc., that sorely need improvements in multithreading. [19:16:36.0180] Yes, pattern matching is a fairly large feature, but I'd argue its more than worth its weight in complexity. I can guarantee I am going to reach for pattern matching about 100x more often than I would `Temporal`, or `bigint`. [19:19:08.0776] And despite using the phrase "complexity", I don't find it to be too complex either. Once you have a grasp of the basics of pattern matching, each feature is complementary with the others. [19:19:38.0579] This is where I found `${}` and `let when` to be so jarring, they felt out of place compared to other constructs. [19:24:35.0281] with pattern matching, you can start simply and build: - `v is String` to perform a simple type test - `v is String or Number` to perform test two types - `v is "foo" or "bar" or 1` to test specific values - `v is { x: Number, y: Number }` to test shapes - `match (v) { ... }` to test multiple branches - `match (v) { { x: let x, y: let y }: ... }` to pull an POJO apart - `match (v) { Point(let x, let y): ... }` to pull a class apart - etc. [19:25:32.0986] By the time you get to the 3rd bullet point, you've already been introduced to `or`. `and`, `not`, and `()` aren't a huge leap beyond that if you're familiar with `||`, `&&`, and `!`. [19:32:12.0511] I could agree that `if` patterns are slightly out of step with the rest of the proposal, compared to an `if` clause. However, having them be patterns makes them more generally useful. With an `if` clause, you have to spend time and energy rearranging your pattern to satisfy the runtime requirements of a trailing `if` (which I mentioned above), while having `if` available as a pattern allows you to keep those (hopefully few) occurrences local to the subpattern that needs them. I hope, though, that the pattern matching syntax itself is expressive enough so as to avoid needing to lean on `if` except for the narrowest of corner cases. [19:37:46.0443] For example: if we do not support relational patterns, you're more likely to need to lean on `if` to perform relational tests, like: ``` match (p) { Point(let x, let y) if (x > 0 && y > 0): "positive x, positive y"; Point(let x, let y) if (x > 0 && y < 0): "positive x, negative y"; Point(let x, let y) if (x < 0 && y > 0): ...; Point(let x, let y) if (x < 0 && y < 0): ...; ... } ``` While relational patterns would remove that need here: Point(> 0, > 0): "positive x, positive y"; Point(> 0, < 0): "positive x, negative y"; Point(< 0, > 0): ...; Point(< 0, < 0): ...; ... } ``` The more expressive the pattern syntax, the less likely you'll actually see `if (x is ... if (y))` in actual code since you won't need that escape hatch. [19:38:17.0656] * For example: if we do not support relational patterns, you're more likely to need to lean on `if` to perform relational tests, like: ``` match (p) { Point(let x, let y) if (x > 0 && y > 0): "positive x, positive y"; Point(let x, let y) if (x > 0 && y < 0): "positive x, negative y"; Point(let x, let y) if (x < 0 && y > 0): ...; Point(let x, let y) if (x < 0 && y < 0): ...; ... } ``` While relational patterns would remove that need here: ``` Point(> 0, > 0): "positive x, positive y"; Point(> 0, \< 0): "positive x, negative y"; Point(\< 0, > 0): ...; Point(\< 0, \< 0): ...; ... } ``` The more expressive the pattern syntax, the less likely you'll actually see `if (x is ... if (y))` in actual code since you won't need that escape hatch. [19:38:37.0317] * For example: if we do not support relational patterns, you're more likely to need to lean on `if` to perform relational tests, like: ``` match (p) { Point(let x, let y) if (x > 0 && y > 0): "positive x, positive y"; Point(let x, let y) if (x > 0 && y < 0): "positive x, negative y"; Point(let x, let y) if (x < 0 && y > 0): ...; Point(let x, let y) if (x < 0 && y < 0): ...; ... } ``` While relational patterns would remove that need here: ``` match (p) { Point(> 0, > 0): "positive x, positive y"; Point(> 0, \< 0): "positive x, negative y"; Point(\< 0, > 0): ...; Point(\< 0, \< 0): ...; ... } ``` The more expressive the pattern syntax, the less likely you'll actually see `if (x is ... if (y))` in actual code since you won't need that escape hatch. [19:38:52.0788] * For example: if we do not support relational patterns, you're more likely to need to lean on `if` to perform relational tests, like: ``` match (p) { Point(let x, let y) if (x > 0 && y > 0): "positive x, positive y"; Point(let x, let y) if (x > 0 && y < 0): "positive x, negative y"; Point(let x, let y) if (x < 0 && y > 0): ...; Point(let x, let y) if (x < 0 && y < 0): ...; ... } ``` While relational patterns would remove that need here: ``` match (p) { Point(> 0, > 0): "positive x, positive y"; Point(> 0, < 0): "positive x, negative y"; Point(< 0, > 0): ...; Point(< 0, < 0): ...; ... } ``` The more expressive the pattern syntax, the less likely you'll actually see `if (x is ... if (y))` in actual code since you won't need that escape hatch. [19:41:07.0028] And that's what `if` patterns are: an escape hatch. Something you should hopefully never need to reach for. [19:41:39.0109] * I could agree that `if` patterns are slightly out of step with the rest of the proposal, compared to an `if` clause. However, having them be patterns makes them more generally useful. With an `if` clause, you have to spend time and energy rearranging your pattern to satisfy the runtime requirements of a trailing `if` (which I described a few days ago), while having `if` available as a pattern allows you to keep those (hopefully few) occurrences local to the subpattern that needs them. I hope, though, that the pattern matching syntax itself is expressive enough so as to avoid needing to lean on `if` except for the narrowest of corner cases. [19:46:13.0600] And if `if` patterns exist and are disagreeable in some places, they can be called out by linters or by syntax highlighters to indicate their unique nature, to call attention to the use of the escape hatch. If you seen a `if` pattern, does that mean a lack of expressivity in pattern matching? Does that mean you should restructure your input to improve its matchability? Does that mean you're trying to do something too complicated and you should refactor to multiple statements? Any one, or all of these could apply. [19:46:33.0318] * And if `if` patterns exist and are disagreeable in some places, they can be called out by linters or by syntax highlighters to indicate their unique nature, to call attention to the use of the escape hatch. If you've seen a `if` pattern, does that mean a lack of expressivity in pattern matching? Does that mean you should restructure your input to improve its matchability? Does that mean you're trying to do something too complicated and you should refactor to multiple statements? Any one, or all of these could apply. [21:27:24.0677] 🤔 should we discuss it on the pattern matching repo? that's more accessible to the whole community [21:41:31.0745] Sure. My hope was that having a "sidebar" would improve communication but that hasn't worked out well; I've mostly felt very stressed out about having to be the lone dissenter [21:43:58.0924] you can publish it on GitHub, maybe there are other guys objecting to this, don't feel too stressed 👀 [23:24:22.0404] > <@rkirsling:matrix.org> So what's getting totally lost is why we want the things that we want. > > `is` feels like "_huh? this is not the proposal I showed up for? we're here to make a better `switch`, not to pull a bait-and-switch_", yet wanting `if (x is Some(let v))` makes a lot of sense. Simultaneously though, it's clear that for the general POJO situation, `if (let { foo } = x)` would integrate better into the language. > > Indeed, the temperature of the committee that I was experiencing last week is that having "pattern matching has a solution for that!" come up in the discussion of a half-dozen disparate proposals is a _worrying_ signal, that the feature has grown unreasonably large. > > Although I was thinking yesterday that the spelling `if` is more of an issue than whether it's a pattern, this may have been mistaken. If `if` is a guard then it feels more natural to exclude it from `is`, which makes my largest concerns disappear. I believe strongly that patterns should be as simple as possible -- it's not even clear to me that `and` is a good idea; the thing that we really _need_ it for is a way to connect a binding to a pattern that's going to use that binding, yet ironically that's exactly the case where `and` is not the most intuitive keyword to read. it's not a bait and switch; we got feedback from SM that we *can't* do a better switch unless we build it iteratively, and `is` makes perfect sense for that [23:25:31.0647] > <@jackworks:matrix.org> 🤔 should we discuss it on the pattern matching repo? that's more accessible to the whole community i don't think more cooks will make for a better broth [23:46:49.0670] right, I understand that that's where `is` came from [23:50:11.0886] the irony is that a `match` statement creates a clearly delineated space in which new stuff can exist without threatening old stuff [23:57:37.0548] as soon as you spill out of those braces and introduce patterns as a first-class concept, we now have to worry about interactions of patterns with literally everything else in the language, which is a _gigantic_ expansion of scope [00:15:51.0072] this means that it's now far harder to reason about the totality of implications at hand, and I'm not totally clear whether SM is supportive of the place we've ended up, even if they encouraged proceeding in this general direction [00:20:04.0220] > <@rkirsling:matrix.org> as soon as you spill out of those braces and introduce patterns as a first-class concept, we now have to worry about interactions of patterns with literally everything else in the language, which is a _gigantic_ expansion of scope I expressed this worry out loud to various people at plenary, and everyone I talked to made me feel like my worry was, if anything, tame [00:20:21.0583] > <@rkirsling:matrix.org> as soon as you spill out of those braces and introduce patterns as a first-class concept, we now have to worry about interactions of patterns with literally everything else in the language, which is a _gigantic_ expansion of scope * because I expressed this worry out loud to various people at plenary, and everyone I talked to made me feel like my worry was, if anything, tame [00:57:33.0942] * because I expressed this worry out loud to various people at plenary, and everyone I talked to made me feel like my worry was, if anything, tame (tame, because what I'm trying so hard to protect is _existing JS_; I _am_ supportive of having a `match` statement, which is not a given!) [02:02:53.0090] i'm a bit confused about the implication of "protecting"; nothing we do here will break existing JS, it's just about existing developers' expectations and intuition, and the likelihood of bugs in both use and refactors, no? [09:02:36.0705] just a meta-comment: i would say currently SpiderMonkey as a whole does not have a fully formed position; I've been attending the meeting to try to provide early forms of feedback such that if we do ultimately conclude we support the proposal, we think the proposal will be in a shape that we can implement with less challenges. As the PR comes together, I'll eventually try to get more of the team to sit down and actually ponder the proposal in more detail. My -personal- feelings are extremely mixed here. Interaction with the champions group has generally helped convince me of various pieces, but my natural inclination tends much closer to r.kirsling. [09:50:41.0561] > <@rkirsling:matrix.org> Seems like we're only looking for people to nod and agree This is an extremely bad read of our response to your feedback. I'm not sure how to restate it; I think I've been as clear as possible that your feedback was acknowledged, but we decided not to take action on it, and I gave the exact reasons why we decided that. I understand if you *disagree* with our conclusion, but casting that as us *ignoring* your feedback is simple wrong, and frankly a bit insulting. I'm not sure how I'm supposed to accept your feedback if the only allowed response is "we agree completely and have changed the proposal accordingly". [09:51:31.0000] And if you think I'm not being kind with that interpretation of your statements, well, same. [09:58:52.0762] I understand the frustration of being a dissenter among a group of supporters, but that is still a valuable position to take. But this proposal has been pulled in many, many directions by various people over the year+ it's been worked on; it absolutely can't satisfy everyone, and there are *strictly contradictory* desires for how it should look among the champions. We'll end up with something that will make everyone at least slightly unhappy vs their ideal version, guaranteed, but with luck we'll all be overall satisfied with the results. [10:00:45.0728] (Like, I still feel *very* strongly that losing `${...}` patterns is a big blow; it prevents us from generating on-the-fly matchers like `${caseless("foo-bar")}` to match the string "foo-bar" ignoring ascii case. Currently we can only dynamically generate matchers *if they're expressible as a regex literal*, which is is a funky restriction. But enough people pushed back on this that I dropped it for now; the rest of the proposal is still valuable and powerful and we can always revisit this in the future.) 2023-10-05 [17:43:03.0377] > <@tabatkins:matrix.org> And if you think I'm not being kind with that interpretation of your statements, well, same. Sorry, this has indeed been quite difficult. What I was seeking in bringing the conversation here is a "step away from plenary and have a calm sidebar chat to resolve concerns" situation but I don't think this chat can actually replicate that; the illusion that it could has probably made this a worse choice than just using a GH issue [18:01:52.0242] > <@ljharb:matrix.org> i'm a bit confused about the implication of "protecting"; nothing we do here will break existing JS, it's just about existing developers' expectations and intuition, and the likelihood of bugs in both use and refactors, no? "protecting existing JS" was just my single-phrase summary of the worry I expressed -- of course we're not going to break the web regardless, but "we now have to worry about interactions of patterns with literally everything else in the language" [18:02:46.0775] > <@rkirsling:matrix.org> combined with combinators and if patterns it's as if we're now not just redoing `switch` but also redoing destructuring, boolean operators, and ternaries; it feels like a bifurcation of the language namely, this [18:06:58.0063] Anyway, this started as me feeling that I must be missing some important rationale lurking behind our current approach, but [18:07:33.0899] if, by this point, it's just me having a different opinion, I'm happy to try to formulate a clear statement of that on GH instead [18:43:38.0559] > <@rkirsling:matrix.org> "protecting existing JS" was just my single-phrase summary of the worry I expressed -- of course we're not going to break the web regardless, but "we now have to worry about interactions of patterns with literally everything else in the language" that was always going to be the case; even if this proposal didn't include it, there's tons of follow-ons waiting in the wings to add patterns in lots of other places [18:53:57.0993] fair enough [18:55:36.0610] I mean, even with that possibility, I think "what's our absolute minimum for day 1 must-haves" is an important thing to have in mind [18:55:51.0286] * I mean, even with that possibility, I think "what's our absolute minimum for day 1 must-haves" is an important thing to have continually in mind [18:57:46.0430] but I think that's sort of my point anyway: this is really shocking scope creep for a single proposal, but it wouldn't have to feel that way if it _weren't_ a single proposal [22:11:32.0112] indeed, I think it's interesting that Python _rejected_ "and" and "not" https://peps.python.org/pep-0622/#and-patterns and deferred "patterns outside of `match`" as well as custom matchers https://peps.python.org/pep-0622/#one-off-syntax-variant [03:48:49.0534] I find the "rarely used" argument for `and` and `not` to be debatable. I've seen them both used quite often in C# [04:54:24.0249] i mean, i would use them a ton in JS. i don't understand why anyone would claim they're rarely used. [05:00:35.0214] languages are different, maybe it's true that python doesn't need it [05:49:11.0477] for me, it just feels a foreign concept, since none of the languages I've used pattern matching in have had that [05:49:28.0407] is it just C# and F#? [05:56:44.0003] the reason it feels "foreign" and not like, filling in a gap, is because you wouldn't expect to destructure something into two different shapes at once in an FP language [05:57:56.0114] kinda feels like a paradigm break, like passing a function with side effects to map [05:59:16.0881] * for me, it just feels like a foreign concept, since none of the languages I've used pattern matching in have had that [06:06:16.0151] > <@rkirsling:matrix.org> the reason it feels "foreign" and not like, filling in a gap, is because you wouldn't expect to destructure something into two different shapes at once in an FP language yeah they can match based on nonimal type, but we're in JavaScript where objects can have arbitrary fields and no meaningful identity [06:08:25.0948] right [06:09:17.0716] so in that sense a language like JS could probably be a reasonable exception (unlike with the `map` example) [06:11:35.0383] Not all pattern matching is destructuring. Also, JS has a number of objects with internal brands (including user-defined objects with private fields) that make it necessary to be able to combine brand tests with other patterns. For example: ``` match (x) { when Map and { size: 0 }: ...; } ``` [06:13:30.0108] mhm [06:13:39.0787] > <@rkirsling:matrix.org> for me, it just feels like a foreign concept, since none of the languages I've used pattern matching in have had that I wonder if we need something like https://rbuckton.github.io/regexp-features/ for pattern matching. Unfortunately, I don't have the bandwidth to do that myself at the moment. [06:16:00.0695] `and` and `not` are also very useful with relational patterns, such as: ``` x is not in y x is not String x is >= 0 and < 10 ``` [06:20:07.0617] > <@rbuckton:matrix.org> I wonder if we need something like https://rbuckton.github.io/regexp-features/ for pattern matching. Unfortunately, I don't have the bandwidth to do that myself at the moment. I wonder if devs really adopt new features in regex ... (I also have the same worry about CSS). [06:21:04.0878] they have the same feeling to me: (1) hard to learn (2) things cannot be done within it being done with JavaScript so developers already ok with it [06:21:27.0951] > <@rbuckton:matrix.org> `and` and `not` are also very useful with relational patterns, such as: > ``` > x is not in y > x is not String > x is >= 0 and < 10 > ``` note that a lot of languages would handle the last one with a range pattern [06:22:19.0418] we decompose it into more basic ideas, isn't it good? 🤔 [06:23:25.0411] > <@rkirsling:matrix.org> note that a lot of languages would handle the last one with a range pattern I'm not opposed to the idea of a range pattern, but `x is > 0 and <= 10` feels like a more natural transition for JS developers, as JS has no other concept of ranges at present (aside from the slice proposal, which has been dormant for awhile) [06:23:58.0466] right it does feel like you'd expect it to go along with some other notion of ranges in JS [06:25:52.0115] I do think range and slice notations would be useful for JS, at some point. I wouldn't want to tack that on to this. [06:27:39.0401] Ranges are also a bit harder to conceptualize compared to relational comparisons, if you're not used to them, especially if you don't have a convenient syntax for open and closed ranges. It makes the `> 0` case harder, especially when dealing with floats. [06:28:00.0192] * Ranges are also a bit harder to conceptualize compared to relational comparisons, if you're not used to them. Especially if you don't have a convenient syntax for open and closed ranges. It makes the `> 0` case harder, especially when dealing with floats. [06:32:07.0465] Strange that python adds grouped patterns, but not `and` or `not`, which are the main reasons you would need grouped patterns. [06:42:53.0895] Also interesting that the rationale for Python to use `_` for discards is the exact opposite of ljharb's position as to why we can't use `_` for discards. 🤷 [06:44:06.0580] > In addition, this would put Python in a rather unique position: The underscore is as a wildcard pattern in _every_ programming language with pattern matching that we could find (including C#, Elixir, Erlang, F#, Grace, Haskell, Mathematica, OCaml, Ruby, Rust, Scala, Swift, and Thorn). Keeping in mind that many users of Python also work with other programming languages, have prior experience when learning Python, and may move on to other languages after having learned Python, we find that such well-established standards are important and relevant with respect to readability and learnability. In our view, concerns that this wildcard means that a regular name received special treatment are not strong enough to introduce syntax that would make Python special. [06:44:21.0199] * > In addition, this would put Python in a rather unique position: The underscore is [used] as a wildcard pattern in _every_ programming language with pattern matching that we could find (including C#, Elixir, Erlang, F#, Grace, Haskell, Mathematica, OCaml, Ruby, Rust, Scala, Swift, and Thorn). Keeping in mind that many users of Python also work with other programming languages, have prior experience when learning Python, and may move on to other languages after having learned Python, we find that such well-established standards are important and relevant with respect to readability and learnability. In our view, concerns that this wildcard means that a regular name received special treatment are not strong enough to introduce syntax that would make Python special. [06:50:18.0517] Also, Python's rationale for not including `and` includes: > Guard conditions can also support many of the use cases that a hypothetical ‘and’ operator would be used for. Which I believe to be counterintuitive. Guards are often harder to use as they require breaking out of the pattern syntax, which is a cognitive context switch, and introduces a physical distance between the pattern and the guard (since guards in Python are a clause and not a pattern as we've been discussing). [06:51:20.0558] > <@rbuckton:matrix.org> Strange that python adds grouped patterns, but not `and` or `not`, which are the main reasons you would need grouped patterns. what do you mean by grouped? [06:51:29.0823] `()` [06:52:11.0765] Parenthesized patterns aren't really necessary if you don't have `and` or `not`, since there's no precedence to consider. [06:52:31.0370] In the PEP, it exists purely as a visual indicator and nothing more. [06:52:39.0360] * According to the PEP, it exists purely as a visual indicator and nothing more. [06:53:56.0879] PEP-635 indicates it's useful with OR patterns, but it really isn't in the absence of conjunction and negation. [06:54:38.0009] Per PEP-634: > A parenthesized pattern has no additional syntax. It allows users to add parentheses around patterns to emphasize the intended grouping. [06:54:49.0582] oh oops [06:54:59.0821] PEP-622 was superceded, huh [06:55:01.0019] So 634 and 635's descriptions are at odds with each other. [06:56:57.0626] It looks like 622 was suspended, and 634 was accepted in 3.10 [06:57:38.0902] * It looks like 622 was suspended and 634 was accepted in 3.10 [07:00:36.0610] if we pick the python position, we will be in a strange situation: all other pattern matching designs create binding directly, but that is what we explicitly avoided (x vs let x) [07:02:06.0497] Python introduces bindings directly, but python doesn't have a variable declaration mechanism. [07:02:59.0265] > <@jackworks:matrix.org> if we pick the python position, we will be in a strange situation: all other pattern matching designs create binding directly, but that is what we explicitly avoided (x vs let x) What do you mean by "all other pattern matching designs"? If you mean within Python only, see my point above. If you mean "in all languages", that is incorrect. [07:04:36.0862] C# has declaration patterns. Rust does not, but Rust's patterns are confusing to the reader regarding bindings because an arbitrary identifier can either be a reference OR a binding, depending on whether that variable already exists. Rust doesn't allow you to have arbitrary dynamic bindings introduces by a global scope. JS does, so JS needs an explicit binding mechanism. [07:06:31.0046] I was really happy to see that Dart went ahead of us with the explicit decls [07:07:33.0287] I need to step away for a bit, so I'll be slow to respond for a few hours [07:10:19.0823] * C# has declaration patterns. Rust does not, but Rust's patterns are confusing to the reader regarding bindings because an arbitrary identifier can either be a reference OR a binding, depending on whether that variable already exists. Rust doesn't allow you to have arbitrary dynamic bindings introduced by a global scope. JS does, so JS needs an explicit binding mechanism. [07:14:08.0080] oh sorry, I only know pm from some fp language and rust, not from C# [10:58:15.0382] > <@rbuckton:matrix.org> Also interesting that the rationale for Python to use `_` for discards is the exact opposite of ljharb's position as to why we can't use `_` for discards. 🤷 Especially now that binding matchers are spelled differently, I think we should revisit this. Under the current proposal syntax, a `_` matcher is just a variable matcher, resolving `_` and either === it, invoking it, or invoking its customMatcher, depending. For the things that `_` is used for (underscore.js, etc), *none* of these are even remotely relevant or expected to be useful. [11:00:51.0763] > <@jackworks:matrix.org> if we pick the python position, we will be in a strange situation: all other pattern matching designs create binding directly, but that is what we explicitly avoided (x vs let x) Python's only got one way to create bindings, and it doesn't have a prefix keyword at all, yeah. (And the same is generally true of a lot of languages.) Some langs, like rust, let you use the decl keyword before the pattern (like my older idea of `let match`) to implicitly denote the binding semantics. [11:02:21.0277] And it's really quite weird, imo, that Rust lets you do `Point { x: 0, y }` to check if the x is equal to 0 (and establish a binding for y), but doesn't let you compare x to a variable. Only literal comparisons are allowed. [11:02:50.0074] Like, I'll often point to other pattern languages for precedent, but I genuinely think this is something a lot of languages have just gotten wrong and copied from each other. [11:04:31.0156] Like, this example shows the usefulness of being able to check against literals, but doesn't ask the question of "what if you wanted to check against literally anything other than 0?". [11:06:28.0622] (But of course you *can* continue to check against variables *if the variable is a type name* or whatever. Just not actual varaible bindings. `Point { x: Some(x), y }` is allowed. Like I said, it's just a weird and I think broken design.) [11:09:44.0071] > <@rbuckton:matrix.org> C# has declaration patterns. Rust does not, but Rust's patterns are confusing to the reader regarding bindings because an arbitrary identifier can either be a reference OR a binding, depending on whether that variable already exists. Rust doesn't allow you to have arbitrary dynamic bindings introduced by a global scope. JS does, so JS needs an explicit binding mechanism. Oh wait, really? So does `Point { x: otherX, y }` check the x coordinate against `otherX` *if it exists*, but otherwise craete an `otherX` binding? [11:11:58.0686] I believe that is the case, yes. See: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#matching-named-variables [11:12:33.0376] Actually, that's not correct, sorry. Rust introduces the binding here, but if `y` was the name of a *type*, I think that's a different matter. [11:13:01.0778] Okay, yeah, that's what I said up above, phew. [11:13:19.0427] So yeah, I still consider it a broken behavior design that a lot of langs just copied from each other. [11:14:05.0820] If you're going for *just* "fallible destructuring", then it makes sense. But as soon as you start being able to compare against values, you need to *be able to compare against values*, not just constants. [11:15:08.0013] And Rust uses `@` to bind and test, or to capture a property into a new name: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#-bindings [11:16:39.0051] Ah, could you write `Point { x: x @ someVal, y }` to test it against `someVal` and establish an `x` binding? Maybe `_ @ someVal` to just do the test? [11:17:16.0625] (Sorry, I don't have a rust environment up to test myself.) [11:18:04.0007] No, you just do `{ x: pattern }` to test. If you need to test against a variable, you have to use an `if` guard. [11:18:30.0586] Ok, so still broken. Good to know. ^_^ [11:20:10.0112] I think the reason `None` works is that `None` is essentially both a type and a value, so : ```rs match x { Some(y) => ..., None => ..., } ``` Isn't matching `x` to `None` as a value, but as an instance of the `None` type. [11:20:48.0942] Yeah I suspect the logic is just "if type, match against it. Otherwise, create a binding." [11:20:55.0852] and enums are types and also sorta values [11:21:24.0023] Which is where the binding mechanism gets confusing. `match x { Foo(None) => ... }` or similar differs from `match x { Foo(bar) => ... }` if `bar` is not a type. 2023-10-06 [19:55:57.0642] Yeah, the `@` comes from Haskell and if my non-JS-specific thinking that we just needed a way to conjoin a binding and another subpattern held, I would've suggested that spelling (although maybe we'd still not want to because of decorators... but whatever, moot point now) [11:02:13.0031] Since we're in pattern-space the connection with decorators would just have been conceptual, not an actual grammar clash, luckily. [11:46:20.0826] I'd be a little wary of that. Decorators are designed to apply to declarations in general, with possible expansion into many areas like functions, parameters, and possibly even variables, so if we used `@` in pattern matching we would be blocking the application of an future proposal to declarations in pattern matching. I don't *think* we'll ever do decorators for variables, but that's only because I haven't come across a convincing use case, not that such a use case doesn't exist. [16:06:05.0029] You'd be decorating the variable, tho, not the let, right? `let @foo val, val2;` or whatever, rather than `@foo\nlet val, val2; [16:06:08.0091] * You'd be decorating the variable, tho, not the let, right? `let @foo val, val2;` or whatever, rather than \`@foo\\nlet val, val2;` [16:18:52.0958] That is unclear. My point is that it's better to avoid the space if there are other options. I also am not a fan of `@` in general for this as it doesn't convey mutability like `let`/`const` and would feel a bit out of place as a declarator in JS 2023-10-07 [23:04:52.0400] I meant that it would be `let foo @ [let head, ...let tail]` [23:11:06.0570] That just looks confusing. If we wanted to use a one character token we could just replace `and` with `&` and write `let foo & [let head, ...let tail]` or `[let head, ...let tail] & let foo` interchangeably [23:14:14.0870] the issue is that there isn't usually a notion of "and" in pattern matching, but there is a notion of "pinning" or "as patterns" or whatever you want to call it [23:17:03.0143] i.e. `[let head, ...let tail] as let foo` would be fine too, it just fixes it to be after instead of before [23:17:59.0664] this is what I'm saying when I say "conjoin[ing] a binding with another subpattern ... is ironically the place where `and` feels like a confusing word choice" [23:19:42.0160] if we need to have `and`, then yes, we don't need another thing for `@` / `as`, but I have never thought of `@` / `as` as being `and`-like [23:20:50.0079] * this is what I'm saying when I say "we ... need ... a way to conjoin a binding with another subpattern, yet this is ironically the place where `and` feels like a confusing word choice" [23:22:09.0222] * if we need to have `and` for independent reasons, then yes, we don't need another thing for `@` / `as`, but I have never thought of `@` / `as` as being `and`-like [23:25:22.0362] * this is what I'm saying when I say "the thing we ... need is a way to conjoin a binding with another subpattern, yet this is ironically the place where `and` feels like a confusing word choice" [23:47:28.0617] > <@rkirsling:matrix.org> the issue is that there isn't usually a notion of "and" in pattern matching, but there is a notion of "pinning" or "as patterns" or whatever you want to call it I have seen `and` patterns about as often as I've seen pinning patterns [23:49:47.0381] I'm not saying `@` or `as` are `and` like, but that I believe `and` should exist independently, and there's no reason to have two different tokens that could just be a single token. Comparing `A and B and let c` vs `A and B @ let c`, the `@` feels like it could also just be `and`. [23:49:59.0874] * I'm not saying `@` or `as` are `and`-like, but that I believe `and` should exist independently, and there's no reason to have two different tokens that could just be a single token. Comparing `A and B and let c` vs `A and B @ let c`, the `@` feels like it could also just be `and`. [23:50:43.0839] ...which I just said, yes [23:50:44.0302] Especially if `@` means you have to spell a lone `let` pattern as `when @ let c` [23:50:56.0972] If you don't need the `@`, then it's just acting like an `and`. [23:52:34.0545] > <@rbuckton:matrix.org> Especially if `@` means you have to spell a lone `let` pattern as `when @ let c` this isn't a thing; pinning exists so that you can destructure while binding to the thing that you're destructuring [23:56:12.0641] I'm kind of wondering where that specific confusion came from 🤔 [23:56:21.0259] Not confusion. [23:56:38.0007] That was the point I was trying to make. `let` patterns are more flexible than a special pinning syntax like `pattern @ let foo` or `pattern as let foo`, assuming you have `and`, because its just another pattern and can fit in whatever order you prefer. [23:57:04.0935] I don't follow [23:58:15.0453] it's `let foo @ ` or ` as let foo`, and if you already had an `and` then you would use `and` for both of these cases and not add a new thing [23:59:32.0300] Yes. I think we're trying to say the same thing. [00:00:10.0020] If we have `and` conjunctions and `let` patterns, then a special pinning syntax is unnecessary. [00:00:29.0673] I didn't understand where the dangling conjoiner suggestion came from [00:01:47.0538] It wasn't a suggestion. I was trying to point out that you *don't* have to include the `@` or `as` if you are only introducing a binding in the syntaxes that have that design. [00:02:36.0273] > <@rbuckton:matrix.org> Especially if `@` means you have to spell a lone `let` pattern as `when @ let c` I mean this sentence [00:02:46.0330] Yes, that's what I am talking about [00:03:07.0349] ignore that sentence and let me rephrase [00:06:48.0136] The point I was trying to make was this: Imagine we have two competing syntaxes to consider: 1. Pinning using `@`: ``` match (x) { when [let x]: ...; // just bind when [let x @ (1 or 2)]: ...; // bind and match } ``` 2. `let` patterns w/ `and` conjunctions ``` match (x) { when [let x]: ...; // just bind (same) when [let x and (1 or 2)]: ...; // bind and match when [(1 or 2) and let x]: ...; // bind and match } ``` [00:07:09.0371] In the case of the "just bind" pattern, both (1) and (2) are essentially the same [00:07:27.0265] So in effect, even in (1), `let x` is a pattern [00:07:39.0374] * The point I was trying to make was this: Imagine we have two competing syntaxes to consider: 1. Pinning using `@`: ``` match (v) { when [let x]: ...; // just bind when [let x @ (1 or 2)]: ...; // bind and match } ``` 2. `let` patterns w/ `and` conjunctions ``` match (v) { when [let x]: ...; // just bind (same) when [let x and (1 or 2)]: ...; // bind and match when [(1 or 2) and let x]: ...; // bind and match } ``` [00:10:43.0193] (1) requires a fixed position for the binding (i.e., to the left of the pattern), and the `@` isn't really used for anything else in the pattern so it's an operator with a narrow focus. [00:13:02.0834] (2) provides more flexibility which could be useful if other future add-ons like default initializers are considered, since there would be an inherent evaluation order and whatever side effects that brings along to consider. In addition `and` has far more utility beyond just "pinning" [00:14:23.0620] `@`'s single-use nature makes me think of Alton Brown's distaste for single-use kitchen gadgets. :) [00:15:19.0487] I mean, flexibility is a complete non-goal for me, but we don't have to rehash understood philosophical differences [00:15:29.0919] if we need `and` then we need `and` [00:15:44.0411] > <@rkirsling:matrix.org> Yeah, the `@` comes from Haskell and if my non-JS-specific thinking that we just needed a way to conjoin a binding and another subpattern held, I would've suggested that spelling (although maybe we'd still not want to because of decorators... but whatever, moot point now) hence this being moot [00:16:58.0534] I should stop debating and turn in for the night. [00:18:26.0055] I'm not really sure how a clarifying statement ended up as a conversation to begin with :( [00:28:05.0525] It's what I get for trying to think when I should be sleeping 2023-10-08 [08:07:25.0724] we have ` { let x }` as a shortcut, I think we should also have `{ let x: }` [08:07:57.0971] consider this example: ```js if (value is { ok: true, value: { id: Number and let id, password: String and let pass } }) { console.log(id, pass) } // vs if (value is { ok: true, value: { let id: Number, let password: String } }) { console.log(id, pass) } ``` [08:08:12.0741] it will be much cleaner if both match and bind is on the way 2023-10-09 [13:29:45.0400] rbuckton: I don't know why, but like half of your comments from a few days ago on the proposal don't give me the ability to reply. [13:56:04.0966] > <@tabatkins:matrix.org> rbuckton: I don't know why, but like half of your comments from a few days ago on the proposal don't give me the ability to reply. because they're replies to an existing thread. if you click on the timestamp, it will jump you up to the canonical thread and you can reply there [13:56:38.0110] Ohhhhh I'd never noticed that UI before, neat. [13:56:51.0130] Wait, that's not true. [13:56:55.0877] Like https://github.com/tc39/proposal-pattern-matching/pull/293#discussion_r1347917753 [13:57:01.0627] (or at least that's how it's supposed to work) [13:57:06.0265] if i click the timestamp that's just a permalink to the message [13:57:31.0151] hm, i can reply to that [13:57:36.0674] * hm, i can reply on that one. are you *sure*? [13:58:06.0169] it's def a permalink normally. but in this case, since the review comment is more like a reference to the in-thread reply, the permalink points to the thread and not to the review comment [13:58:13.0768] ??? The one starting "I strongly believe we should add it in both places"? [13:58:15.0811] * it's def a permalink normally. but in this case, since the review comment is more like a reference to the in-thread reply, the permalink points to the in-thread comment and not to the review comment [13:58:22.0245] I'm staring at the comment right now. [13:58:43.0929] yes, this is what the link you pasted into element shows me: [13:59:11.0749] * yes, this is what the link you pasted into element shows me: (i'm using safari, but that shouldn't matter) [14:01:43.0461] Hm, if I load the link directly it does take me to the thread rather than the individual message. [14:02:02.0909] okay, github just being a little fucked up about UI, no surprise there [14:02:37.0952] it might be that the original thread is hidden, so the jumping only works when all things are manually shown [14:02:52.0897] hm, maybe 2023-10-16 [07:41:10.0126] are we meeting today? [07:54:07.0593] Planning on it [08:06:57.0239] ljharb: [08:09:05.0165] I can’t attend; I’m at a conference today and tomorrow [08:31:40.0415] Short meeting, notes up at https://github.com/tc39/proposal-pattern-matching/issues/307 [08:51:18.0256] TLDR: After the new README merge, I will rewrite the new spec. I opened an issue to write down *all* features we have rn https://github.com/tc39/proposal-pattern-matching/issues/306. 🤔 in the issue means we don't have generally agreement but the idea has been raised with reasonable use case. I don't want to ship an MVP, I want to have as many useful features as possible, therefore I'll go through this approach: add features as removable features. It's also important to let the committee know this. If we are over our syntax budget, we can remove some of them instead of getting the whole proposal rejected. Here are removable features I'd like to have (they are all explained in https://github.com/tc39/proposal-pattern-matching/issues/306): - Default value (`{ a = 1 }`) - Unary algebraic pattern (`+x`, `-y`) - RegEx literal pattern (`/a/`) - RegEx named capture group bindings (`/a(?)/`) - Relation pattern (`>` `<` `>=` `<=` `in` `instanceof` `===`) - Void pattern (`[void, 2]`) - Optional property pattern (`{ a: 1, b?: 2 }`) - Binding-while-matching property pattern (`{ let a: Number }`) This seems to be a lot of syntax sugars but they're all removable. I'll remark this clearly in both README and spec. If anyone wants to add more to the list or strongly object to some ideas to be written as removable in the spec please let me know! BTW I'll explain new things that are not formally denoted before. This is all my personal thought, e.g. Rbuckton might disagree with me despite he also supports relational patterns. Why does relational pattern include the `===` pattern? Because for pattern `{ a: x }`, it actually has 2 possible semantics: `===` or custom matcher. If we want to force it to do `===` matching, there is no way to do it therefore `{ a: === x }` might be a useful thing although looks strange. What's the use case for the Binding-while-matching property pattern? For code doing a lot of shape checks and creating bindings (this is common for handling unknown data), this increases readability. ``` // without when { type: String and let type, id: (Number or String or null) and let id, let action }: // with when { let type: String, let id: Number or String or null, let action } ``` [09:45:58.0603] Is `===` necessary for relational patterns if we're already using SameValue/SameValueZero? `instanceof` is marginally useful depending on the final semantics for the default behavior for an implicit custom matcher of `class`, since it is otherwise subsumed by `x is y`. [09:47:18.0658] I'd argue for `===` it's such a narrow corner case that I'd be fine relegating it to the `if` clause/pattern escape hatch. [16:05:00.0927] Once you have `>`, etc, *not* having the equalities is weirder than not having it imo 2023-10-17 [17:49:15.0543] * Once you have `>`, etc, _not_ having the equalities is weirder than having it imo [20:11:59.0775] > <@rbuckton:matrix.org> Is `===` necessary for relational patterns if we're already using SameValue/SameValueZero? `instanceof` is marginally useful depending on the final semantics for the default behavior for an implicit custom matcher of `class`, since it is otherwise subsumed by `x is y`. let's say x is a custom matcher, then you cannot match with `=== x` because x will be treated as a custom matcher. `{ z: x }` 2023-10-20 [10:14:20.0839] Yup, that too [10:15:11.0455] I'd considered it a "slightly annoying but not too important" loss that you couldn't check, say, that the subject was a particular function (because you'd instead be invoking the function), but `=== fn` does indeed bring that back. [10:27:34.0526] I'm not certain how often that will come up in practice, and we could also have `x is y` do a Same Value check first before trying the custom matcher [10:28:31.0465] Except when doing `x is y()`, since that can only be a custom matcher [11:41:04.0694] The more exceptions we add the more fragile things become. [11:41:49.0285] That would mean you can't match a subject that's a function against a predicate that might be the same function, for instance. [11:42:07.0163] Rare? Sure. A weird wrinkle that'll confuse the pants off of someone running into it? Absolutely. [11:42:47.0097] I'd rather have `===` patterns and keep plain variable matchers with their existing set of wrinkles. [11:49:02.0029] Wait nm, got it backwards - you wouldn't be able to execute a predicate against itself as a subject. [11:49:38.0615] And fixing *that* would require a lot more contortions in some new feature, versus the current behavior which you can switch out of with `===`. [14:13:17.0536] Couldn't you disambiguate the rarer case of matching a function against itself by doing something like `x is y(...)`? [14:15:20.0780] I'll be clear. I'm not opposed to having `===` or any other equality check, they just seem rare enough in practice that we could get by with not having them and using `if`, if there is pushback. [14:17:47.0377] I just don't want `<`, `>`, `>=`, `<=`, and `in` to be lumped together with `===` if someone blocks `===`, as there is a huge difference in how often `<` et al will be used in comparison. [14:40:27.0627] I mean, it's not necessarily that rare, and definitely not something you'd know you need to invoke. [14:41:09.0342] Like if you have `match(x) { String: ...; }` are you going to anticipate the possibility of `x` being the String object itself? [14:41:21.0145] Nah, you're using `String` to mean "is a string". [14:41:51.0235] I'd much rather just *not solve the problem* of matching against a function, than add this additional wrinkle/footgun. [14:42:25.0378] * Like if you have `match(x) { String: ...; }` are you going to anticipate the possibility of `x` being the String object itself, and thus passing that test? 2023-10-21 [21:37:12.0519] > <@rbuckton:matrix.org> Couldn't you disambiguate the rarer case of matching a function against itself by doing something like `x is y(...)`? no I cannot think of, but as a language feature, I'd like to keep the property that you can always do === match. this makes it feel "complete" [21:38:45.0176] > <@rbuckton:matrix.org> I just don't want `<`, `>`, `>=`, `<=`, and `in` to be lumped together with `===` if someone blocks `===`, as there is a huge difference in how often `<` et al will be used in comparison. yes, I'll make the separation between `>` and `===` so readers will know `===` can be removed and `>` still be kept if they don't like `===` [05:24:07.0136] That should be fine. `===` and `>` belong to different productions in the expression grammar as well, so it makes sense to separate them here 2023-10-23 [10:22:33.0060] the meeting for the 30th is showing up an hour later on the calendar - i don't think it's due to the time change since that's not until 11/5. any objections to me moving it back to 8AM PT? 2023-10-24 [08:11:50.0298] time changes happen at different times in different countries [08:12:00.0974] its likely due to the meeting being originally created in germany [08:12:19.0729] we change time on oct. 29th [08:12:58.0920] i don't think i will make it, im wiped out by splash. so no objection from me [10:59:12.0428] ahhh ok, makes sense, thanks, i didn't realize anyone but the US had a time change [10:59:17.0853] i'll move it 2023-10-28 [00:35:58.0830] daylight saving time? 2023-10-29 [12:06:00.0909] spec progress: - PrimitivePattern - MemberExpressionPattern - UnaryAlgebraicPattern - CombinedMatchPattern - RS: Custom matchers - RS: All built-in matchers - Class default matchers To be done: - Match expression - is expression - ObjectPattern/ArrayPattern - ExtractorPattern - BindingPattern (`let`) - RegExLiteralPattern - RelationalPattern - VoidPattern 2023-10-30 [06:47:28.0740] I have to miss meeting today. [08:02:51.0578] I’ll be there in a couple minutes. [08:09:15.0339] I think it is cancelled. I can't see it in my calendar. [08:11:04.0327] it's def on the calendar [08:12:15.0428] 🤦 wrong calendar. [08:55:13.0186] i appear to have dropped, sorry, brb 2023-10-31 [12:21:33.0835] expand to a reasonable set of allowed MemberExpression