2022-07-05 [19:10:40.0385] should we try to advance to stage 2? next meeting only have 5 days to the deadline [08:00:48.0533] that would be great if we have sufficient spec text [09:06:05.0541] > <@jackworks:matrix.org> Missing parts: > Static Semantics (including early errors, BoundName stuff) > @@matcher > Builtin matchers šŸ‘† [09:06:45.0103] but I guess built-in matchers will be hard to specify [09:30:42.0169] i don't think most of them will be [09:31:08.0949] eg, Boolean's matcher is "does it have a [[BooleanData]] internal slot", etc [09:31:22.0492] the only one that's tricky will be the error subclasses, because those will be like "has a [[ErrorData]] internal slot, and also "constructor is %TypeError%" or something [09:31:38.0476] brand check is a big topic in tc39 [09:31:49.0650] we do brand checks all over the place [09:32:13.0854] * the only one that's tricky will be the error subclasses, because those will be like "has a [[ErrorData]] internal slot, and also "constructor is %TypeError%" or something [09:32:20.0329] like, a ton of places [09:39:09.0672] Do we have a list of early errors? [09:41:26.0076] "everything that's invalid syntax" :-p i don't think we made a concrete list [09:59:27.0148] > <@ljharb:matrix.org> "everything that's invalid syntax" :-p i don't think we made a concrete list I mean, for example, the top level identifier pattern can only appear at most once and at the end [10:00:07.0139] that's probably one of the few that we're artificially applying (as opposed to, falls out of the syntax) 2022-07-06 [06:52:00.0177] šŸ‘€ we only have 3 days until deadline. do we want to advance it? [07:53:26.0816] i still say yes :-) what more do we think we need in the spec text for it to be sufficient? [07:57:52.0088] early errors. I think a non-normative list will be enough, we can normatively specify it later. [09:10:38.0300] what early errors are there besides the "fake else must be in the else position" one? [09:14:30.0955] > <@ljharb:matrix.org> what early errors are there besides the "fake else must be in the else position" one? I don't know, this is why I need help šŸ˜‚ [09:15:06.0551] lol [09:15:13.0123] maybe let's just make the list of 1 right now? [09:15:22.0546] the spec doesn't have to be perfect, just initial 2022-07-07 [22:47:49.0608] Oh I remember [22:48:02.0216] pattern `a | b & c` is an early error [02:38:37.0678] ok I believe the spec is mostly completed [02:38:46.0616] added early errors & built-in matchers [02:39:03.0330] https://tc39.es/proposal-pattern-matching/ [02:56:19.0410] The built in matchers are very problematic. I believe it will be a blocker of this proposal. [02:57:15.0229] Problems: subclassing Error, Map, Array, Set Promise AsyncFunction/AsyncGeneratorFunction [02:57:46.0044] Also, the wrapper object of primitive types [07:14:11.0869] subclasses will have the right internal slot [07:14:20.0761] as do boxed primitives [07:14:24.0616] * as do boxed primitives [07:14:36.0938] I’m not sure why it’ll be difficult; I’ll try to make a PR today [16:04:51.0519] k, i've pushed up a bunch of spec fixes; i'm working on the builtin matchers now 2022-07-08 [20:44:56.0556] i filed https://github.com/tc39/proposal-pattern-matching/pull/263 - i'm going to merge it later this evening if anyone wants to comment in the meantime, and then i'll file the relevant issues and add the agenda item for stage 2. (we can always retract the request later) [22:22:35.0397] ok - i've merged that PR, and updated https://github.com/tc39/proposal-pattern-matching/issues/175 to point to the 5 open questions, all of which i consider stage 2 concerns. there's 3 spec TODO comments for me that i'll get to in the coming days. If there's additional TODOs, please file an issue, or, push up an editor's emu-note with a TODO in it. i've also added "pattern matching for stage 2" to the agenda with a 60 minute timebox, with me to present. Please directly push up a change to the agenda if you'd like to help me present, and please discuss here if the timebox should be longer or shorter. [22:23:15.0488] anyone who wants to volunteer to make the slides would also help me out; i'm much better at battledecks then i am at making content :-) (cc mpcsh) [23:07:29.0811] > <@ljharb:matrix.org> i filed https://github.com/tc39/proposal-pattern-matching/pull/263 - i'm going to merge it later this evening if anyone wants to comment in the meantime, and then i'll file the relevant issues and add the agenda item for stage 2. (we can always retract the request later) I see the new spec it's better! [23:22:36.0186] awesome, thanks! [23:47:23.0123] I think we can reuse the slide last presented [00:15:26.0558] i was hoping so, with minor edits, and I’ll do that if nobody gives me anything better :-) [11:15:54.0228] Jack Works: does https://tc39.es/proposal-pattern-matching/#prod-MatchPropertyList (and MatchElementList) allow for an optional trailing comma? [11:16:08.0659] * Jack Works: does https://tc39.es/proposal-pattern-matching/#prod-MatchPropertyList (and MatchElementList) allow for an optional trailing comma? [11:25:40.0421] > <@ljharb:matrix.org> Jack Works: does https://tc39.es/proposal-pattern-matching/#prod-MatchPropertyList (and MatchElementList) allow for an optional trailing comma? I copied the spec of deconstructing, so it should be the same [11:26:03.0977] * I copied the spec of deconstructing, so it should be the same [11:27:56.0102] hm, k [11:30:19.0671] i see the unobservable WeakMap and Set - any reason that's not a Record of Lists? [11:34:27.0606] It's easier to express the semantics. Because I need a prop-value cache which might not be simple to specify using Record/List [11:43:37.0100] is SameValue the right cache key to use tho? [11:43:59.0828] for properties, it's just strings and symbols, and for iterables, if it yields the same thing twice it should still appear twice [11:44:58.0217] i totally get why you'd use a weakmap ofc, just wondering if it'd work almost as easily to use a Record [11:47:43.0831] same question with IteratedItems, why an array instead of a List [11:48:39.0433] > <@ljharb:matrix.org> same question with IteratedItems, why an array instead of a List Because I cannot store a spec list on a ECMAScript object [11:50:10.0434] And for every matchable it will have its own cache (to keep track of iterable/property access), so (to be stored in a WeakMap) the cache is an ES value too [11:50:43.0814] * And for every matchable it will have its own cache (to keep track of iterable/property access), so (to be stored in a WeakMap) the cache is an ES value too [11:56:24.0414] ahh i see, so you want the ES object keyed, and Records can't do that [11:56:35.0579] and once you have a weakmap, you can't put spec values there. [11:56:36.0207] makes sense [11:58:19.0492] I can make a new ES object with an internal slot that stores Record, but it seems unnecessary since there are already so many ES values [12:06:49.0244] yeah i agree [12:07:03.0600] a spec Map that can key on ES values would work, but that's also a lot 2022-07-10 [20:39:18.0632] https://twitter.com/littledan/status/1545619478385491968 [20:39:34.0453] 😄 looks like people don't like pattern matching [09:09:33.0147] actually seems pretty positive to me [09:10:05.0138] naysayers are always the loudest and there’s very few and ā€œoverengineeredā€ is the worst feedback - which implies they don’t understand all the use cases :-) 2022-07-11 [19:40:49.0797] Yeah that feedback doesn't look unreasonable. I feel very safe ignoring "overengineered", since we're actually triangulating reasonably well on a minimal (but complete) feature set; that is indeed just people not realizing the use-cases we're trying to hit. [20:19:01.0218] I'm still unhappy that `${x}` would likely block extractors in the future because of the `id{x}` conflict with `$` as a legal identifier. I'm hoping to present on extractors and the relation to pattern matching and ADT enums in a future TC39, but I'm in the middle of a cross-country move currently so I didn't have much time to prepare anything for next week's plenary. [20:23:39.0975] Do you have a link to extractors again? [20:24:44.0775] I was thinking about putting together an extractor proposal that just relied on `Symbol.matcher` rather than a separate symbol with a similar purpose. That would allow this syntax instead of the one in the explainer that uses `with`: ```js const result = Option.Some{ value: 10 }; const Option.Some{ value } = result; // extractor is dual/inverse of constructor ... match (result) { when Option.Some{ value }: console.log(value); // also dual/inverse here when Option.None: console.log("none"); } ``` [20:25:20.0731] I haven't updated it in a bit, but the gist is here: https://gist.github.com/rbuckton/ae46b33f383ba69880c7138c49b5e799 [20:32:40.0595] One reason for the design sketch for the proposal was to have a common syntax to rely on between ADT enums, destructuring, and pattern matching: ```js enum Message of ADT { Quit, Move{ x, y }, Write(message) } const msg = Message.Move{ x: 10, y: 10 }; const Message.Move{ x, y } = msg; // ok, declares x and y and initializes values const Message.Write(y) = msg; // throws error since x is not a Message.move (i.e., result returned `{ match: false }`) match (msg) { when Message.Quit: process.exit(); when Message.Move{ x, y }: console.log(`move by x:${x}, y: ${y}`); when Message.Write(message): console.log(message); } ``` Where the declaration `Move{ x, y }` matches construction `Message.Move{ x: 10, y: 10 }`, destructuring `const Message.Move{x, y} = ...`, and pattern matching: `when Message.Move{ x, y }: ...` [20:34:09.0983] * One reason for the design sketch for the proposal was to have a common syntax to rely on between ADT enums, destructuring, and pattern matching: ```js enum Message of ADT { Quit, Move{ x, y }, Write(message) } const msg = Message.Move{ x: 10, y: 10 }; const Message.Move{ x, y } = msg; // ok, declares x and y and initializes values const Message.Write(y) = msg; // throws error since x is not a Message.move (i.e., result returned `{ match: false }`) match (msg) { when Message.Quit: process.exit(); when Message.Move{ x, y }: console.log(`move by x:${x}, y: ${y}`); when Message.Write(message): console.log(message); } ``` Where the declaration `Move{ x, y }` matches construction `Message.Move{ x: 10, y: 10 }`, destructuring `const Message.Move{x, y} = ...`, and pattern matching: `when Message.Move{ x, y }: ...` [20:37:25.0713] The `id{}` syntax would also be a useful fit for a general-purpose object construction mechanism (possibly with its own built-in symbol rather than call/construct), so you could potentially have something like: ```js element.style.border = CSS.Borders{ top: '1px solid black', left: '1px solid black' }; ``` [20:43:22.0757] `${}` doesn't "have" to block `id{}`, since `id {}` could still be legal, but it would be confusing to allow `x{}`, `x {}`, and `$ {}` but not `${}` because of its collision with `${}` in `match`. [07:59:51.0148] rbuckton (PTO: 7/5 - 7/16): So for extractors, I'm still eh on the {} arglist part even separate from this `${}` conflict. Having a keyword-only constructor seems just as annoying as having the indexed-only constructors we have today. I'd rather pursue a keyword+ arglist syntax, a la Python's. [07:59:53.0774] But I do see the reason you have it existing - as a parallel to the `{}` destructuring pattern. [08:02:11.0595] And your nested-extractors example is compelling for why going with something similar to match's `with` syntax isn't great. [08:03:31.0094] I'm not clear on what you mean by "keyword only constructor" [08:08:52.0474] `Move{x:1, y:1}` - listed as an enum constructor and possibly a normal-object constructor [08:12:32.0861] And hm, not *ideal*, but I suppose today's answer for keyword args - just pass an object - would work for extracting too. Just return a single-item array from unapply containing the object. [08:17:10.0670] `const Message.Move({x, y}) = foo()`, yeah that doesn't look awful to me? [08:20:33.0770] > <@rbuckton:matrix.org> I'm still unhappy that `${x}` would likely block extractors in the future because of the `id{x}` conflict with `$` as a legal identifier. I'm hoping to present on extractors and the relation to pattern matching and ADT enums in a future TC39, but I'm in the middle of a cross-country move currently so I didn't have much time to prepare anything for next week's plenary. There is still ThisKindOf(pattern) extractor left for you [08:23:25.0807] That just adds more overhead, and potentially overcomplicated the ADT pattern for structured enum members: ``` const msg = Message.Move({ x: 10, y: 10 }); msg[0].x; // 10 // vs const msg = Message.Move{x: 10, y: 10 }; msg.x; // 10 ``` 2022-07-12 [21:27:10.0477] I found this part is not correct. Step 7 should not use `?` otherwise it will not close iterators [09:53:43.0956] good catch; want to put up a PR to fix it? the grammar stuff is more your domain than mine :-) [12:07:04.0576] any reviews on https://github.com/tc39/proposal-pattern-matching/pull/273 ? [16:35:58.0734] Jack Works: in addition to the step 7 PR, any chance you'd be able to put up a PR to handle a) adding semicolons between match clauses, and b) to require parens on or/and combinators? (see https://github.com/tc39/proposal-pattern-matching/issues/275#issuecomment-1182580760 ) i'm not entirely sure how to fix it 2022-07-13 [20:51:24.0843] that's also hard for me šŸ˜‚ [20:51:30.0432] lol oops [20:51:42.0613] not sure if anyone of the champion group is better at spec grammar [20:51:57.0487] i doubt any of this is a stage 2 blocker fwiw, but if we can rush in the fixes before plenary that'll help [20:52:14.0269] I can fix RS problems now [20:52:20.0271] awesome [09:53:42.0925] TabAtkins: review on https://github.com/tc39/proposal-pattern-matching/pull/273 ? i'd like to land it before making another PR for the "universal subclassing" accomodations 2022-07-14 [11:03:58.0664] FYI: Useful info from pre-meeting among Google folks: [11:04:42.0222] * Waldemar is hesitant about complexity (no surprise) but won't block since he knows others find it useful. Will very likely bring up specific syntax concerns, which we've been pretty good at addressing. [11:06:06.0036] Specifically, the "no separator between end of one clause and start of another" bit (third item in his OP of issue 275) is bothering him - I don't think we have any objections to ending clauses with semicolons and allowing ASI to generally make them omittable? [11:08:10.0719] * Might get pushback on the regex literal matcher. I consider it an important matcher, but not critical - we could drop it if it's required. However, I'd prefer to push back if the complaint comes up; the person who gave the feedback had apparently never written code that was "regexes in an if-else chain", so didn't understand the use-case. Considering that precise use-case was one of the major reasons Python added the `:=` operator, it's *definitely* highly prevalent. Still, this is potentially a topic we can relent on if necessary, imo. [11:11:27.0752] * Might get pushback on the lack of an automatic instanceof check. This is *extremely* easy to write a custom matcher for (especially with the bool handling now - `static [Symbol.matcher](val) { return val instanceof MyClass; }`), so we've avoided it partly to *appease* people concerned about feature bloat, but this is also a place I think we could relent on if necessary. Either doing it automatically (if the result of the interpolation pattern is a non-primitive without a matcher method, automatically do an instanceof check instead of a === check), or with syntax (`instanceof ${...}` as grammar?). [11:23:32.0347] i'm 100% on board with requiring semicolons (and letting ASI fix the error of omitting them) [11:23:51.0391] > <@tabatkins:matrix.org> * Might get pushback on the regex literal matcher. I consider it an important matcher, but not critical - we could drop it if it's required. However, I'd prefer to push back if the complaint comes up; the person who gave the feedback had apparently never written code that was "regexes in an if-else chain", so didn't understand the use-case. Considering that precise use-case was one of the major reasons Python added the `:=` operator, it's *definitely* highly prevalent. Still, this is potentially a topic we can relent on if necessary, imo. i'd love to hear more about this pushback, was it just "doesn't seem useful to me personally"? [11:23:57.0411] PR 279 adds that, in a way that's robust for builtins but effectively uses instanceof for userland classes. the current approach there uses a new slot; i'm currently authoring an alternative approach that avoids adding a new slot to every object. [11:24:15.0603] > <@tabatkins:matrix.org> * Might get pushback on the lack of an automatic instanceof check. This is *extremely* easy to write a custom matcher for (especially with the bool handling now - `static [Symbol.matcher](val) { return val instanceof MyClass; }`), so we've avoided it partly to *appease* people concerned about feature bloat, but this is also a place I think we could relent on if necessary. Either doing it automatically (if the result of the interpolation pattern is a non-primitive without a matcher method, automatically do an instanceof check instead of a === check), or with syntax (`instanceof ${...}` as grammar?). * PR 279 adds that, in a way that's robust for builtins but effectively uses instanceof for userland classes [11:24:37.0229] * PR 279 adds that, in a way that's robust for builtins but effectively uses instanceof for userland classes. the current approach there uses a new slot; i'm currently authoring an alternative approach that avoids adding a new slot to every object. [11:24:54.0923] Yes, the pushback was generally "this looks like unnecessary complexity and I don't get why it's there" [11:25:13.0903] and when I gave the example of a chain of if/else with regexes, the response was "i've never seen code that like before" [11:25:45.0398] since multiple folks will respond with "i write/read that kind of code all the time", i'm not concerned with that pushback [11:26:16.0090] yeah, and i'd like to push back with that exact argument, just giving a heads-up and my approval of "yeah we can drop it" if it proves necessary [11:27:37.0507] Real easy to write a custom matcher for it if it proves necessary, after all. just `function Regex(re){ return val=>({matches:re.test(val), value:re.exec(val)}); }` [11:28:10.0802] RegExp.prototype would have one anyways [11:28:18.0184] so you'd just `${/whatever/g}`, and you'd have to explicitly name bindings with `with` [11:28:30.0731] * so you'd just `${/whatever/g}`, and you'd have to explicitly name bindings with `with` [11:29:11.0207] Oh right, we'd just drop that in there since adding those is "free" complexity. ^_^ [11:29:46.0644] Re: 279, ah, I didn't realize that's what you were doing there. So the idea is that'll work for all classes, not just built-ins, automatically doing effectively an instanceof check? [11:43:28.0733] yes [11:43:33.0636] i just pushed up the latest commit; TAL [12:22:47.0582] ljharb: I see how the new AO would work to check something is a subclass, but I'm not seeing how it's invoked for arbitrary things, just a number of built-ins. Is there something I'm missing that will make `${Foo}` automatically instanceof-check the matchable to be a Foo or subclass, if there's no matcher manually specified? [12:30:30.0563] oh right - that's why i had Object set up this way (cc Jack Works ) [12:30:34.0745] * oh right - that's why i had Object set up this way (cc Jack Works ) [12:31:03.0732] so basically, anything that extends from Object, or any non-primitive-wrapper builtin, will inherit a matcher that will work [12:31:38.0029] so if you do `class Foo {}` and then `${Foo}`, it will grab the Object matcher, which (after i update it) will walk the prototype and detect that the instance's `.constructor === Foo`, and return true [12:31:47.0969] I assumed I'd see something like that, yeah, just didn't see it. [12:32:26.0497] it's there in the MatchConstructorInstance AO - note, not in main, in PR 297 [12:32:40.0362] oh wait, in `class Foo {}`, `Foo` inherits from `Function`, so it will just work as-is (with PR 297) [12:33:25.0893] * it's there in the MatchConstructorInstance AO - note, not in main, in PR 297 [12:33:33.0864] * oh wait, in `class Foo {}`, `Foo` inherits from `Function`, so it will just work as-is (with PR 297) [12:35:09.0664] Yeah, I'm looking at 297. It won't work, because the Function checker still opens with an check for callables, immediately failing if the matchable isn't callable. [12:36:13.0332] oh right, snap [12:36:15.0226] hmm [12:36:24.0737] class constructors are callable tho [12:36:31.0150] they just call into %ThrowTypeError% [12:36:43.0754] the matchable is what's checked, aka the class instances, not the constructor [12:36:47.0508] the constructor just holds the matcher [12:36:51.0655] oh right [12:36:53.0871] hmm [12:37:08.0307] ok so that's a tricky thing to figure out then [12:37:38.0407] Right now, `match(function(){}) { when(${RandomClass}): ...; }` will succeed [12:38:04.0208] why? there's no Function.prototype matcher [12:38:32.0291] Okay so I'm *incredibly* confused now. [12:38:35.0158] ohh sorry i get what you mean [12:42:31.0968] ok so when a function is a matchable, `${RandomClass}` certainly shouldn't match [12:42:50.0885] it should match `Function`, but not "any function" [12:43:10.0373] Yeah you're right, I was mistaken. [12:43:30.0015] Oh no, wait, it still will. [12:43:54.0393] Becuase *one* of the checks if it the matchable (or a prototype) matches the given class, but the *other* is whether the matchable (or a prototype) matches the Function intrinsic. [12:44:18.0365] which random functions do, afaict [12:47:26.0147] I suspect what we might want to do instead is make the intrinsic argument optional, and only do the intrinsic check if it's passed, and then *not* pass the Function intrinsic's name in the Function matcher. [12:47:39.0670] right, hmm [12:47:53.0510] for Function, specifically, that might make sense [12:47:59.0534] Yes, only for that. [12:48:05.0236] altho, hmm [12:48:08.0851] wait [12:48:19.0872] if the `this` of the matcher is === Function, i think we do want to pass the intrinsic name [12:48:33.0599] I'd be fine with that, yes. [12:48:43.0427] and in that case, we also check callability? [12:49:07.0111] but if the `this` is not Function, then we do NOT check callability, and we omit the intrinsic name [12:49:16.0166] It'd get caught anyway so long as the matchable and the `Function` instance were from the same realm, but I'm fine making it as reliable as the other intrinsics. [12:49:25.0386] yeah [12:49:39.0365] oh right, i could instead of checking SameValue check for "any realm's %Function%" [12:54:17.0149] k, updated [12:56:20.0446] swap the order of the last two lines in the Function matcher, you're omitting/passing the intrinsic in the opposite cases [12:57:14.0348] otherwise lgtm after that fix [13:18:03.0389] nm, not lgtm, i dropped a comment with the correct fix. [13:18:32.0546] (This was the matcher on Function itself, which random classes would *not* inherit. We still need to add a matcher (the generic one) on Function.prototype, instead.) [14:02:08.0524] TabAtkins: ok, i think i updated it to do the right thing this time, TAL? [16:20:56.0053] hi friends! sorry I've been so MIA — I'm rather underwater right now and need to unbury myself. I'll catch up on the above conversations soonā„¢ļø. I had a great conversation today with my colleague Willian Martins, and we discussed his upcoming stage 1 [catch guards](https://github.com/wmsbill/proposal-catch-guards) proposal. at its core, the goal of the proposal is twofold: 1. provide the ability to pattern-match inside what is currently the binding portion of a `catch` 2. provide the ability to _selectively_ catch — i.e. without a default case, no error would be caught. this obviates the common "check if we want to catch, then re-throw if not" pattern. I caught Willian up on the current state of the pattern matching proposal, and we agreed that the best course of action, if you all are on board, is to merge this with pattern matching. our current `catch match` formulation doesn't provide anything beyond saving a level of indentation, but I think we can include Willian's proposal here in a really elegant way. specifically: use `catch match` as the keyword, allow a pattern in the parens, and then have the RHS simply be a `catch` RHS, rather than a `match` statement body. that way, we can pattern-match at what is currently the binding level of a `catch`, we don't have to introduce yet more keywords, and we can introduce the nice short-circuit re-throwing that Willian thought of. I'll write this up as a PR, but I just wanted to take the temperature of the room. thoughts? TabAtkins ljharb Jack Works yulia danielrosenwasser rkirsling [16:21:18.0478] * hi friends! sorry I've been so MIA — I'm rather underwater right now and need to unbury myself. I'll catch up on the above conversations soon:tm:. I had a great conversation today with my colleague Willian Martins, and we discussed his upcoming stage 1 [catch guards](https://github.com/wmsbill/proposal-catch-guards) proposal. at its core, the goal of the proposal is twofold: 1. provide the ability to pattern-match inside what is currently the binding portion of a `catch` 2. provide the ability to _selectively_ catch — i.e. without a default case, no error would be caught. this obviates the common "check if we want to catch, then re-throw if not" pattern. I caught Willian up on the current state of the pattern matching proposal, and we agreed that the best course of action, if you all are on board, is to merge this with pattern matching. our current `catch match` formulation doesn't provide anything beyond saving a level of indentation, but I think we can include Willian's proposal here in a really elegant way. specifically: use `catch match` as the keyword, allow a pattern in the parens, and then have the RHS simply be a `catch` RHS, rather than a `match` statement body. that way, we can pattern-match at what is currently the binding level of a `catch`, we don't have to introduce yet more keywords, and we can introduce the nice short-circuit re-throwing that Willian thought of. I'll write this up as a PR, but I just wanted to take the temperature of the room. thoughts? TabAtkins ljharb Jack Works yulia danielrosenwasser rkirsling [16:21:26.0679] * hi friends! sorry I've been so MIA — I'm rather underwater right now and need to unbury myself. I'll catch up on the above conversations soonā„¢ļø. I had a great conversation today with my colleague Willian Martins, and we discussed his upcoming stage 1 [catch guards](https://github.com/wmsbill/proposal-catch-guards) proposal. at its core, the goal of the proposal is twofold: 1. provide the ability to pattern-match inside what is currently the binding portion of a `catch` 2. provide the ability to _selectively_ catch — i.e. without a default case, no error would be caught. this obviates the common "check if we want to catch, then re-throw if not" pattern. I caught Willian up on the current state of the pattern matching proposal, and we agreed that the best course of action, if you all are on board, is to merge this with pattern matching. our current `catch match` formulation doesn't provide anything beyond saving a level of indentation, but I think we can include Willian's proposal here in a really elegant way. specifically: use `catch match` as the keyword, allow a pattern in the parens, and then have the RHS simply be a `catch` RHS, rather than a `match` statement body. that way, we can pattern-match at what is currently the binding level of a `catch`, we don't have to introduce yet more keywords, and we can introduce the nice short-circuit re-throwing that Willian thought of. I'll write this up as a PR, but I just wanted to take the temperature of the room. thoughts? TabAtkins ljharb Jack Works yulia danielrosenwasser rkirsling [16:27:15.0906] I absolutely don’t think we should merge in catch guards [16:27:23.0011] that risks tanking the entire proposal [16:27:33.0355] i think it should be a follow on once we’re at stage 3 [16:28:44.0994] I’ve also spoken to Willian; i already have been planning such a follow on proposal, and intentionally have been waiting til pattern matching has advanced. [16:29:59.0514] yeah adding more seems like no regardless of anything else [16:30:04.0129] * yeah adding more seems like a no regardless of anything else [16:40:48.0397] Yeah no problem as a follow-on, hard reject as a merge-in at this stage. [16:42:05.0450] ljharb: I don't think we still need the `Function` special-case in the Function.prototype matcher, right? If the interpolation pattern is `${Function}` it'll already be invoking the `Function` matcher, rather than walking up the prototype to get the `Function.prototype` matcher. [16:42:14.0710] Is there a case I'm missing that makes it still necessary? [16:42:47.0046] * ljharb: I don't think we still need the `Function` special-case in the Function.prototype matcher, right? If the interpolation pattern is `${Function}` it'll already be invoking the `Function` matcher, rather than walking up the prototype to get the `Function.prototype` matcher. [16:44:01.0512] TabAtkins: if the matchable is `Function`, it'd hit the Function.prototype matcher, and without the special case, it wouldn't work on cross-realm Function contructors. [16:44:14.0338] * TabAtkins: if the matchable is `Function`, it'd hit the Function.prototype matcher, and without the special case, it wouldn't work on cross-realm Function contructors. [16:44:44.0723] Yeah, but that's only if the *pattern* was already something other than `Function`. [16:44:52.0573] oh wait right sorry [16:45:08.0955] it's so `Function.prototype[Symbol.matcher].call(Function)` does the right thing [16:45:50.0123] but also with `match (Function) { when ${function () {}} … }` [16:45:58.0760] But why is that important? The `Function` matcher doesn't live on Function.prototype. It's on Function itself, like any other class. [16:46:06.0614] it's a super edge case but there's no reason not to make it work [16:46:15.0361] We don't expect `MyClass.prototype[Symbol.matcher]` to work in general [16:46:25.0994] bc it'll be grabbing some matcher completely unrelated to `MyClass` [16:46:35.0136] that depends on how it's implemented [16:46:55.0848] if MyClass implements its own robust matcher, then it wouldn't work. but it absolutely should work if the matchable is `{ constructor: MyClass }` (with the default function prototype matcher) [16:47:08.0264] * if MyClass implements its own robust matcher, then it wouldn't work. but it absolutely should work if the matchable is `{ constructor: MyClass }` (with the default function prototype matcher) [16:48:04.0893] That's an accident of implementation, tho, I'd think. It's required that a matchable like that *works*, given the spec we've written, but if we could cleanly avoid it we would. [16:49:25.0317] i'll think more on it, but it feels like we need a "base case", and Function is the base [16:49:48.0360] specifically because we omit the `*"%Function%"*` intrinsicName, that every other builtin constructor uses [16:50:01.0724] ohhh wait i think i thought of one [16:50:17.0991] `class F extends Function {} match (new F()) { when ${Function} … }` needs to work [16:50:27.0651] and it needs to work even if F's Function is in a different realm [16:50:47.0334] and without that special case, it would work in same-realm (because `===`) but not cross-realm [16:51:22.0994] or hmm, maybe that's not an issue because as a derived class, F would inherit from Function? [16:51:55.0635] Yeah, it'll pick up the Function matcher. [16:52:02.0576] ok - so maybe we're fine then [16:52:04.0742] Since you inherit your super's statics [16:52:18.0604] hmm [16:53:03.0226] alrighty, i'll remove the special handling and i'll just be super thorough with test cases :-) [16:53:22.0439] kk ^_^ 2022-07-15 [09:56:55.0481] r+ on the latest version of 279, looks good to me now [11:00:58.0616] i'd love to get everyone's opinion in the champion group on 279 as well as 278, ideally before plenary next week (mpcsh danielrosenwasser yulia rkirsling Jack Works ) [11:01:52.0326] * i'd love to get everyone's opinion in the champion group on 279 as well as 278, ideally before plenary next week (mpcsh danielrosenwasser yulia rkirsling Jack Works ) [11:02:48.0671] Folks -- i will have to sadly step away as a champion [11:03:12.0362] I don't have the time to fully dedicate to the proposal, and in its current shape i consider it to be too complex and trying to do too many things [11:03:33.0655] i have had that concern for a long time, ive tried to raise it but it didn't feel like i could represent that accurately, so i am not longer really a champion in practice anyway [11:11:28.0864] yulia: Respect your decision, but I would still be interested in learning more about the "too complex" - I know you had that complaint earlier, but I've not been able to fully understand it, given the similarity in complexity (imo) to the corresponding constructs in many other languages. [11:12:17.0020] I've just gotten out of a lot of meetings and im freshly back from having covid so im not sure ill be able to articulate it well now either [11:12:31.0158] but: i don't think that the complexity in other languages warrents such a complex initial design [11:13:39.0781] we should consider how to break up the proposal into smaller steps -- the argument has been "its all or nothing" and i disagree. This can have great improvements to JS if we do it in parts [11:15:48.0091] Yeah, no pressure right now, but I'd love to hear some more detailed articulation of your feelings, because we've heard "too complex" and I can't reasonably respond to it at that level (since I think it's not). [11:25:46.0024] To be a little more specific, I don't believe the proposal *can* in any meaningful way be broken up into smaller steps. You need the match() construct itself, and at least *most* of the match patterns, to have a useful feature. In theory we can boot a few bits to extension proposals, but in practice that would just draw out the feature's definition without actually reducing anything, since I feel they have very strong arguments for inclusion. [11:26:27.0622] I'm curious if the "smaller steps" is along the lines of "do less interpolation patterns in teh base proposal" or something more radical in cleaving the proposal into smaller pieces? [12:43:15.0620] Let me come up with a clear realistic statement and bring it to you Monday. [13:13:20.0291] I do share Yulia's general sentiment but I agree that it's not concrete enough to be actionable so lemme give this a good think this afternoon too [13:23:20.0109] I have a concrete suggestion, I want time to think of how to convert it properly [13:30:58.0673] * I think I have a concrete suggestion, I want time to think of how to describe it properly [13:54:54.0335] Excellent thanks y'all [13:55:07.0557] (btw I'll be on vacation next week so I'm missing the meeting unfortunately, but I'll follow up) [14:40:54.0362] Okay, so one good thing is that some stuff I thought was intended for V1 is not [14:41:23.0012] (for any proposal you have I will always be in favor of shrinking it lol) [15:05:47.0423] re-reviewing the README, my thoughts are: 1. *General terminology* looks šŸ’Æ 2. *More on combinators*: example makes it looks like `and` is required for existence checking even if the bound name goes unused. This needs fixing but isn't a problem with the proposal. 3. *Array length checking*: "oh btw, bare guards exist" šŸ˜› tbh, I don't think they should exist for V1 -- I think the proposal is leaner if all LHSes start with `when` and `default`, as "general terminology" promised. 4. *Regex*: I kind of hate that both of these are valid. My immediate reaction is that named capture is nifty and `/.../ with ...` is gross. Is the latter a requirement? I don't see why any built-in type needs to motivate the custom matcher protocol. 5. *Custom matchers*: Me being me, I want to ask "could this be a second proposal?" but of course the answer is no, because how can you have a pattern matching proposal that doesn't let you implement Option. So actually I'm totally fine with this. šŸ˜„ 6. A final nitpick: I really dislike the word choice "chaining" for `with`. How about something like "unwrapping"? [15:07:36.0954] 3: if bare guards don't exist, people will write `when (_) if (…)` just to get them. why is that better? [15:08:08.0268] 4: `/…/ with ` is something that unavoidably falls out of "you can use `with` after patterns" [15:08:39.0734] points 2 and 6 seem like good readme feedback, i'm not sure about "unwrapping" but we can bikeshed :-) [15:08:45.0089] * 4: `/…/ with ` is something that unavoidably falls out of "you can use `with` after patterns" [15:08:48.0990] * 3: if bare guards don't exist, people will write `when (_) if (…)` just to get them. why is that better? [15:09:08.0054] 5: the other reason it can't be a separate proposal is that adding it later would be a breaking change, so we'd never be able to (at least, not with builtins, since they'd already have `===` behavior) [15:09:18.0107] * 5: the other reason it can't be a separate proposal is that adding it later would be a breaking change, so we'd never be able to (at least, not with builtins, since they'd already have `===` behavior) [15:10:28.0484] * 3: if bare guards don't exist, people will write `when (throwaway) if (condition)` just to get them. why is that better? [15:11:10.0213] (fwiw i'd have replied in threads if you'd posted each point as a separate message :-p ) [15:12:43.0941] Yeah I kind of thought that might be the response for (4)... For (3), I hear what you're saying but I guess my concern is kind of about people misusing `match` just to do an `if`. I'd probably be less bothered by a case where the `if` came in between two `when`s, say. [15:13:19.0571] remember that this is in a world where `do {}` exists [15:13:25.0725] Ah yeah sorry about the singular message. Not really used to using Matrix on my phone [15:13:42.0537] * Ah yeah sorry about the singular message. Not really used to using Matrix on my phone [15:13:53.0120] so to make if an expression, why would someone do `match (x) { if (condition) { … } }` when they can do `do { if (x === condition) { … } }`? [15:14:14.0164] altho i guess maybe they'd want to to avoid the `x ===` repetition. but if that's what they want, so what? people already abuse switch for that [15:14:20.0137] that seems like linter territory to me [15:15:11.0626] it'd be really easy to block match statements that have no `when`s, with the `no-restricted-syntax` rule [15:15:12.0103] agreed; to the extent that we'd likely end up supporting it sooner or later anyway, it is indeed linter territory. [15:16:38.0838] readme stuff I can create PRs for [15:17:00.0458] overall I'm happy that my concerns are way less significant than I'd remembered them to be [15:21:04.0579] (a lot of people get really excited dreaming up what could be, but I instead get anxious about clutter and waste) [15:21:18.0953] * (a lot of people get really excited dreaming up what could be, but I instead get anxious about clutter and waste) [15:22:14.0613] Or just `a ? b : c`... [15:22:46.0757] My biggest concern is still `${}` [15:29:02.0183] Especially since the old pin/placeholder operator was potentially more convenient and didn't step on `id{}`: i.e., `^Foo` or `^(expr)`, esp since `^()` is the same number of characters as `$ {}` in the worst case. [16:08:19.0084] i think it's incumbent on you to present a proposal to the committee if that ends up being a concern you think should be considered for pattern matching [16:32:16.0821] Er uh, maybe "`with` patterns" is enough? It doesn't necessarily need to be a gerund, just something other than "construct" or "clause" [16:40:41.0941] > <@ljharb:matrix.org> i think it's incumbent on you to present a proposal to the committee if that ends up being a concern you think should be considered for pattern matching I hope to be ready to present in the next plenary following this one. Unfortunately, I'm currently in the middle of a cross country move and wasn't able to finish preparing presentation materials. [16:49:59.0626] i get that, but i think you've been referring to this unapply thing and its interaction with `${}` since november 2022-07-16 [17:12:29.0097] Its been a long process, unfortunately. We sold our house in December, but moved nearby till my middle daughter finished high school. Now we're on the second half of the move. I prioritized some of the smaller proposals I've been working on in the meantime since I've been so randomized with the move. [19:32:57.0791] > <@ljharb:matrix.org> 4: `/…/ with ` is something that unavoidably falls out of "you can use `with` after patterns" Not true - we don't *have* to allow `with` after all patterns. We're doing so because "why not?", but the only patterns we *need* it after are the custom matchers. We could absolutely restrict if we wanted to. [19:33:54.0002] But I don't think we *should* restrict it from regex literals, or else authors would have to decide between "convenient literal syntax" and "access to the match object" whenever they write a regex (and potentially change rewrite their choice if it didn't matter too much at first), which seems like a silly choice to impose on them. [19:52:48.0566] I mean, your whole consistency argument about needing top level irrefutable patterns is the same as here [19:53:04.0901] if we have any pattern without with, we don’t need top level identifier patterns either [20:18:36.0644] but yes, i agree with what you’re saying also [20:24:07.0320] er hang on though [20:24:40.0811] like, there might be a question of whether its an error to stick `with` there but that wasn't really what I was talking about [20:25:17.0459] like, we're having to make it so that regex in particular returns the match result, no? [20:25:43.0628] as a special treatment that we're not doing for any other built-in type [20:26:10.0030] coming purely from a usage standpoint, that's how it looks. but maybe I'm missing something [08:09:05.0737] regex literal patterns provide the match object, and named capture groups as bindings, yes. it’s not the only one tho, because object destructuring and iterable destructuring patterns also provide extra bindings. i suppose regex literal patterns are the only literal pattern that doesn’t provide ā€œthe matchableā€ as the result [08:09:57.0097] but even if we didn’t have regex literal patterns, that behavior (minus the automatic bindings) is exactly what RegExp.prototype’s built-in matcher would do (would need to do, to be useful) so i’m not sure why we’d want to avoid the special form 2022-07-18 [05:38:53.0239] Would you folks be willing to hear a pretty significant modification to what you are proposing? As you might recall -- i suggested we use this as the basis for a new "epic" process [05:39:08.0351] I can present it, just for the champions. I wish we had discussed earlier that this was going for advancement [05:39:26.0743] * I can present it, just for the champions. I wish we had discussed earlier that this was going for advancement [05:58:52.0184] I want to hear that, what is it? [08:42:45.0947] So, the situation is i can no longer type. [08:42:53.0800] or rather: its extremely painful [08:42:55.0705] i did what i could [08:43:02.0492] but everything else needs to be communicated verbally [08:46:42.0352] https://github.com/codehag/pattern-matching-epic [08:46:53.0940] https://docs.google.com/document/d/1dVaSGokKneIT3eDM41Uk67SyWtuLlTWcaJvOxsBX2i0/edit [08:54:02.0396] ljharb: i destroyed an already injured wrist to write that. i have one hand left. read before commenting. [08:55:25.0981] I did read the entire thing. [08:55:45.0065] it was posted 4 minutes ago [08:55:45.0409] theres over 20 pages [08:55:45.0813] you did not [08:55:50.0415] i understand if you aren’t able to type, and i don’t want you to hurt your wrist, but I’m not sure how else to respond [08:55:57.0918] not like this? [08:56:12.0421] The Google doc is 8 pages, and i did read it - I’m a very fast reader. [08:56:23.0691] a poor one [08:56:52.0341] ill just block then [08:57:03.0134] perhaps so. This conversation seems to have rapidly gotten hostile, so I’ll withdraw for now. Suffice to say that while i empathize with your difficulty typing it doesn’t seem reasonable to block if you’re unable to explain your position. [08:57:19.0393] i did, in many many pages [08:57:27.0508] that were dismissed within 2 minutes [08:57:41.0972] that itself is hostile [08:57:41.0993] I’ll give it another read, it’s likely i missed something. I didn’t dismiss all of it - i only responded to one point. [08:59:04.0518] i tried to write the spec but i feel the tendons in my hands. this is fully layerable. all it needs is to have the custom matcher base in the foundation step. or, have it opaque so it can be added later [09:00:08.0982] what is the benefit to doing it in multiple steps? That’s how we did classes, and imo that was a mistake [09:00:23.0153] see the top part of the doc [09:00:54.0488] i also missed that implementation and educaton will benefit [09:01:22.0984] I see the heavy mental load part - that definitely seems to be worth discussing the syntax. that doesn’t address why implementing it over a longer time period will result in a different outcome. [09:01:34.0461] see above [09:02:06.0505] ime education doesn’t benefit from multiple combinations of available features - it benefits from everything landing all at once. That’s why es6 is much easier to teach ime than the myriad of feature matrices since. [09:02:17.0514] * ime education doesn’t benefit from multiple combinations of available features - it benefits from everything landing all at once. That’s why es6 is much easier to teach ime than the myriad of feature matrices since. [09:03:13.0142] anyways i don’t want to stress your wrists further so I’ll try to stop responding [09:03:40.0283] i disagree about education. its covered in the syntax conflation [09:03:51.0841] this feature in particular benefits [09:04:12.0675] please. take time to read it [09:04:21.0239] i will certainly reread it. [09:04:39.0604] (this also sounds like a stage 2 concern, not a stage 1 concern) [09:04:50.0433] this is stage 2 entry concern [09:05:09.0554] problem & shape [09:14:41.0472] could you open the doc for commenting? [09:15:10.0848] > <@yulia:mozilla.org> https://github.com/codehag/pattern-matching-epic this new Epic proposal process looks interesting. I believe Module Loader group will need this, and they're actually doing this in https://github.com/tc39/proposal-compartments/pull/71/files. [09:15:43.0054] can you pass the process to him? [09:16:10.0285] i will work on it more broadly, but i just cant do any more today (or likely this week) [09:16:33.0867] yes [09:18:24.0649] > <@ljharb:matrix.org> could you open the doc for commenting? i can, but i am afraid you will do the same fast response as you did [09:18:46.0075] and, i would like you to first think about everything [09:19:03.0082] also none of this is proposed as final [09:19:33.0227] my response will be the same whether i do it in minutes or days, but sure [09:19:58.0541] i recommend to take more time with things. you obviously did not read before commenting before [09:20:07.0137] and the result was, i was deeply hurt [09:20:19.0400] and you were not correct [09:20:28.0168] please don’t presume what is ā€œobviousā€ about someone else’s actions; you are not correct either. [09:20:37.0285] so the interaction was not worth while, and it really made me reconsider if this is worth taking any more time on [09:20:57.0890] it is obvious you didn't read. you asked questions that were covered [09:21:00.0957] i am also deeply hurt that after months and years in the champion group, you dropped a bomb the day before plenary with topics you haven’t brought up before to my recollection. [09:21:13.0840] you also didn't tell me you were taking it to stage 2 [09:21:16.0665] but I’m still trying to receive the feedback. [09:21:28.0791] i did bring this up actually [09:21:35.0638] and i brought up introducing an epics process in june [09:21:37.0205] it was ignored [09:21:44.0966] i rushed to finish this, knowing that i haven't had time for this [09:22:01.0661] so i did my best [09:22:06.0100] it wasn’t ignored - i can’t speak for the others, but i do not think it’s an appropriate process for this feature. [09:22:08.0986] and i was greeted with "oh i read it in 2 minutes" [09:22:34.0661] it is unfortunate that someone else’s reading speed is interpreted as a personal attack. Not sure what else to say. [09:22:53.0342] a speed read where you end up asking questions that are covered in what you read [09:23:09.0958] this is not comprehension, its discarding another persons work [09:23:14.0284] I’d invite you to consider that ā€œcoveredā€ is subjective, and that perhaps i asked because it was not covered in a way i understood. [09:23:40.0166] there is no way that you read through, and thought about everything, in 2 minutes [09:23:56.0367] ljharb yulia both of you need to walk away from the computer. [09:24:00.0322] I look up to both of you. [09:24:01.0124] stop [09:24:04.0116] fair enough. [09:26:57.0403] I'm behind on ~everything~, I've got a few weeks worth of chat log to read through in this room, so I have more or less no idea what transpired here. not passing judgment or taking sides — as, again, no idea what happened — but going off of this current screen of messages both of you should log off and clear your heads. [09:27:15.0300] * stop. [09:46:14.0159] * ill just have to block then for now. my issue from the june meeting that this should not be so large has not been addressed. If you don't want my feedback, i can withdraw it. [09:53:11.0265] > <@ljharb:matrix.org> so if you do `class Foo {}` and then `${Foo}`, it will grab the Object matcher, which (after i update it) will walk the prototype and detect that the instance's `.constructor === Foo`, and return true Isn't this the opposite behavior of `instanceof`, which uses hasInstance or checks whether the constructor's prototype is in the [[Prototype]] chain? Some libraries patch obj.constructor to hide constructors from consuming code. [10:20:08.0776] I read the whole document and the alternative repo Yulia sent. > what is the benefit to doing it in multiple steps? That’s how we did classes, and imo that was a mistake Yulia means: Do it in multiple steps but **ship it at once**. It does not the same as what we did in the classes. I already see this stratagem in the Module Loader proposals group, and that looks promising. https://github.com/tc39/proposal-compartments/pull/71/files > dropped a bomb the day before plenary if something is doing wrong, we should not continue if we're just "close to the meeting", anyway I don't mean the current proposal is "doing wrong", but I believe Yulia's concern is worth more re-consideration. > Assignment, test, aliasing I never think of this problem before because I'm very familiar with the status quo proposal, but once Yulia pointed this out, I agree this is a serious problem we need to reconsider. Even with today's JavaScript, I am sometimes confused in the deconstruction alias syntax `{ a: b }` which is the binding I can use? If we add one more overload to the `{ ... }` it indeed will make things much harder to read. > `let val when Option.isSome` At the first glance, this makes me think of rbuckton's unapply proposal. I'm much in favor of that form in the past, `let Option(val) = expr`. But after reading the whole document, I'm worried about if unapply syntax could cover all the abilities in Yulia's document. It's late 1 am here so my brain is not very clear and this whole document+conversation is a lot of information for me. I like Yulia's new design, it _does_ solve some concerns from the JSCIG meeting. Please consider it seriously. [10:23:06.0248] (I also send it to average-level friends who knows the current proposal, and here is the reply) [10:24:25.0605] Translation: I don't know too much about his concern (test & assignment, ...), but as a normal user, excepts `isOk` and `maybeRetry.bind(this)`. I agree Yulia's version is much readable. [10:24:57.0671] (I'm gonnna sleeep [10:29:38.0354] yes - we don't want to use instanceof semantics, because we want to support builtins cross-realm [10:39:55.0203] > <@ljharb:matrix.org> yes - we don't want to use instanceof semantics, because we want to support builtins cross-realm I'm not sure how this behavior is any better? It still does a reference comparison for constructor. I'm not sure I'm a fan of wildly divergent behavior since it breaks expectations. [10:47:37.0418] only if the expectation is that it's instanceof semantics, which is highly undesired [10:47:58.0535] you should be able to do `${Map}` and match against Map (or subclass) instances from any realm [10:48:11.0995] instanceof is broken; we shouldn't propagate that breakage further [10:49:08.0807] fwiw i'd probably be fine calling `Symbol.hasInstance` on non-builtin constructors when present; that doesn't hurt anything [11:16:48.0087] > <@ljharb:matrix.org> instanceof is broken; we shouldn't propagate that breakage further Unless you're proposing a solution that works across the board, I don't agree that this should differ except in specific cases. i.e., I'm fine if it differs slightly for builtins (Object, Array, Function, Map, Set, Promise, etc.) but not for user constructors. I have code that wouldn't be able to use `match` if you use `obj.constructor === Foo` [11:18:52.0487] do you use hasInstance to make that work? [11:40:04.0353] The issue is a case like this: ```js export class Base { ... static create() { const somethingInternal = ...; return new Derived(somethingInternal); } } // I only want the module to be able to create instances of Derived to protect/isolate `somethingInternal` class Derived extends Base { constructor(somethingInternal) { super(); // use somethingInternal } } // Don't allow consumers to create new instances of Derived by walking the prototype chain: Derived.prototype.constructor = Base; // works Base.create() instanceof Base; // true Base.create() instanceof Derived; // true // wouldn't work match (Base.create()) { when ${Derived}: ...; // doesn't work because obj.constructor is never `Derived` when ${Base}: ...; // works } ``` [13:14:45.0529] i suppose we could compare the [[Prototype]] of the instance to the .prototype of the constructor for non-builtin constructors, and that would match instanceof. but realistically i think in this case you'd want to add a custom matcher anyways [15:11:13.0200] Your suggestion is to add a custom matcher that just does this: ```js class Derived extends Base { static [Symbol.matcher](obj) { return obj instanceof this; } } ``` Which is, I imagine, what folks would have expected to happen to begin with. I agree that `instanceof` semantics are often bad, but they're what we have in JS. Having `match` do something different without introducing a simple infix operator that does the same seems like a bad idea. The JS community has long had to fight with the idiosyncrasies of `instanceof` and `typeof`, but I'm not sure `match` is the place where we "fix" them? It just makes it inconsistent. [15:12:18.0041] We could just as easily have made `Symbol.hasInstance` on built-ins support `instanceof` cross-realm, or potentially introduced an infix `is` as an `instanceof` replacement. [15:14:44.0467] C#'s `is` is also a pattern-matching expression, though with a limited subset of C#'s pattern matching dialect. [15:18:27.0493] > <@rbuckton:matrix.org> We could just as easily have made `Symbol.hasInstance` on built-ins support `instanceof` cross-realm, or potentially introduced an infix `is` as an `instanceof` replacement. Though this is obviously a breaking change, a similar behavior could be introduced via something like: ```js Object.crossRealmType = { [Symbol.hasInstance](value) { return typeof value === "object"; } }; ... obj instanceof Object.crossRealmType; // uses `Symbol.hasInstance` ``` [15:22:22.0910] While it feels like `match` "fixes" the problems with `instanceof`, if its just a one-off solution that is inconsistent with the rest of the language it starts to feel like more of a wart than a solution a year or two down the line. [15:50:08.0881] yes, and not doing so was another mistake - ES6 has many of them. [15:50:24.0123] For Map, for example, the check shouldn't match instanceof - it should match "can i call `.get` etc on it" [15:50:26.0536] * For Map, for example, the check shouldn't match instanceof - it should match "can i call `.get` etc on it" [15:50:49.0571] that requires a brand check, and instanceof is both insufficient and provides false positives [16:35:05.0272] I would concur with this position in the long term. I'd initially hoped a similar layering approach would work for pipeline with F# pipes+PFA, as each proposal could be independent but layered together. It felt like it would be easier to explain as well: `x |> F` is roughly `F(x)`, and `F~(?, arg)` is roughly `x => F(x, arg)`, so `x |> F~(?, arg)` would then follow from unary function application. The layering approach didn't work there, partially because the reality of the pipeline mechanism forced us towards Hack-style. Pattern matching is, by its very nature, a fairly complex capability. I'm not sure a piecemeal, layered approach is necessarily the right direction, though I do believe we need to consider more consistency with patterns across the language (hence extractors and the `is` operator I mentioned above). [16:36:13.0812] > <@ljharb:matrix.org> ime education doesn’t benefit from multiple combinations of available features - it benefits from everything landing all at once. That’s why es6 is much easier to teach ime than the myriad of feature matrices since. * I would concur with this position in the long term. I'd initially hoped a similar layering approach would work for pipeline with F# pipes+PFA, as each proposal could be independent but layered together. It felt like it would be easier to explain as well: `x |> F` is roughly `F(x)`, and `F~(?, arg)` is roughly `x => F(x, arg)`, so `x |> F~(?, arg)` would then follow from unary function application. The layering approach didn't work there, partially because the reality of the pipeline mechanism forced us towards Hack-style. Pattern matching is, by its very nature, a fairly complex capability. I'm not sure a piecemeal, layered approach is necessarily the right direction, though I do believe we need to consider more consistency with patterns across the language (hence extractors and the `is` operator I mentioned above). 2022-07-19 [22:21:19.0161] I haven't read all contents of yulia's proposal yet, but I like the direction, solve my many syntax concerns. [22:34:38.0743] The current proposed syntax, to be honest, have too much noise and strangeness, need new rules which never in js language, in many cases, I find no benefit than using libs like https://github.com/gvergnaud/ts-pattern . [22:35:01.0168] > <@rbuckton:matrix.org> I would concur with this position in the long term. > > I'd initially hoped a similar layering approach would work for pipeline with F# pipes+PFA, as each proposal could be independent but layered together. It felt like it would be easier to explain as well: `x |> F` is roughly `F(x)`, and `F~(?, arg)` is roughly `x => F(x, arg)`, so `x |> F~(?, arg)` would then follow from unary function application. The layering approach didn't work there, partially because the reality of the pipeline mechanism forced us towards Hack-style. > > Pattern matching is, by its very nature, a fairly complex capability. I'm not sure a piecemeal, layered approach is necessarily the right direction, though I do believe we need to consider more consistency with patterns across the language (hence extractors and the `is` operator I mentioned above). +1, I support PAPP+F# pipe [22:35:39.0325] I also support PAPP+F# pipe :-) [14:06:58.0504] having seen the approach in the loader: https://github.com/tc39/proposal-compartments -- i think this is quite nice, and a good alternative reprsentation 2022-07-20 [12:18:27.0785] I wanted to add my 2 cents here. I've similarly been less engaged with the current proposal. Part of that is because of time constraints, but additionally, it's felt like the proposal iterated so quickly in a direction I wasn't confident about, that I didn't want to be a lone dissenter on the matter. When I came here to say that on Friday, I saw Yulia already had. So when I came back to this chat yesterday and saw https://github.com/codehag/pattern-matching-epic, I was actually pleasantly surprised. While the proposal is currently a strawperson of how pattern matching could be built up bit-by-bit, each of those building blocks resonates a lot with me. A few examples: 1. explicit binding syntax, while seemingly repetitive, makes things clearer, avoids adding an interpolation syntax, and inverts value-matching to a higher priority to binding-introduction (which I think is likely the better direction as a default). 2. while neither proposal is _stellar_ for tagged unions, I'm more amenable to the "epic" proposal variant for it so far (partially thanks to prioritizing value-matching) 3. subsuming switch, while tempting, might do more harm than good. I'm also not convinced that behaviors like fallthrough are consistently undesirable 4. the compositional possibilities of a standalone match are exciting I think there's a lot to consider in that proposal, and I suspect my team would be more amenable to it. 2022-07-22 [16:32:32.0505] Between now and the next meeting I hope to flesh out my Extractors proposal. I think it has the potential to bridge some of what yulia | Out unil July 26th has proposed in the pattern-matching-epic proposal and the current proposal. I've outlined some of the differences here: https://gist.github.com/rbuckton/e49581c9031a73edd0fce7a260748994 [16:32:51.0485] * Between now and the next meeting I hope to flesh out my Extractors proposal. I think it has the potential to bridge some of what yulia | Out unil July 26th has proposed in the pattern-matching-epic proposal and the current proposal. I've outlined some of the differences here: https://gist.github.com/rbuckton/e49581c9031a73edd0fce7a260748994 [16:46:52.0627] I've also updated https://gist.github.com/rbuckton/ae46b33f383ba69880c7138c49b5e799 to use `Symbol.matcher` and improve alignment with the current pattern matching proposal (though this is still a WIP) [16:47:08.0314] * I've also updated https://gist.github.com/rbuckton/ae46b33f383ba69880c7138c49b5e799 to use `Symbol.matcher` and improve alignment with the current pattern matching proposal (though this is still a WIP) [16:51:23.0231] Given how closely Extractors would tie into pattern matching, I am not certain whether it should be a standalone proposal, or should more closely integrate into the pattern matching proposal as part of Yulia's "Epic" process. That said, there are cross-cutting concerns with the `enum` proposal (specifically ADT enums) that may warrant this being standalone. 2022-07-23 [19:02:51.0321] i think there’s a number of proposals that should be done separately even if they layer neatly on top of the core [19:52:14.0434] that definitely includes pipeline [19:52:32.0449] * that definitely includes pipeline 2022-07-26 [14:39:16.0786] I'm also *very* surprised to see a completely different approach dropped immediately prior to the meeting. [14:40:29.0758] I'm also *consistently* bewildered at all these comments about this being strange and unfamiliar, when the majority of the proposal's syntax is pretty much exactly in line with how every pattern matching syntax I'm aware of works. (Haskell and Python, at minimum.) [14:42:10.0367] It's fairly similar to Rust's, as well (tho Rust focuses more on Enum unapplication rather than having a default "list" and "object" to rely on, but it's otherwise very similar) [14:48:13.0936] I've given Yulia's proposal two readthrus now, and I have some feedback. [15:05:40.0035] Okay, having reread Yulia's proposal a few times, some feedback: [15:06:45.0187] 1. The let-when form is pretty cool! A bit problematic in that if the check fails it returns an empty object, which can't be destructured past the first layer unless you go to town with default in the destucturing blob, tho. [15:08:28.0253] If this is something we want (and it sounds nice, especially in the additional forms like integrating into for-of, etc), we could also do this backwards, starting from the current proposal and allowing a let-when to use the proposal's pattern syntax; it just wouldn't establish any bindings (unless you did `let when`, as in your 4th stage). [15:12:39.0873] 2. Because bare idents in the pattern syntax are taken as functions and/or custom matchable objects, there's some *very* unfortunate shenanigans needed to match against variables. We argued over this *exact* topic for quite a while, and the result was our custom matcher syntax `${}`. I think this is very important, honestly - requiring weird prototype hacks to just match a value against something in a variable seems very bad. [15:16:43.0967] Note that your proposal's treatment of bare idents means that the `let {...} when {...};` and `let when {...}` forms are actually *completely different matcher syntaxes*. The former treats bare idents as functions/custom matchers, and *never* as bindings; the latter treats bare idents as bindings, and *never* as functions/custom matchers. Unless I'm completely misunderstanding this? [15:20:14.0060] Or, wait, maybe I *am* misunderstanding things. Is it the case that a `when ` calls the function/custom matcher, but a `when `/etc does a destructure (without actually creating any bindings) and all the names are useless except to mark other bits, like array length in `when [foo, bar]`? [15:21:10.0350] Looking over the examples it does look like that's the case, which means you can't ever run a matcher on a subvalue, only ever the top-most value. This is a huge problem - *every* pattern-matching syntax I've ever seen that allows custom matching logic *at all* allows the logic to be run on values at any level of the pattern. [15:22:48.0917] Well, hm, still confused, because several other examples have matching beyond destructuring (comparison against literal values, or against an or-pattern of literals) nested deeper within a pattern. [15:24:33.0844] So I simply can't tell what nested patterns are allowed to do or what syntax they're meant to use. [15:28:11.0392] My conclusion is that your proposal runs directly into problems we ran into early in the design, and talked over with you and others in design meetings, and then fixed in our proposal in, afaict, a consistent and predictable way. It brings up a useful bit of new functionality - the let-when statement and associated constructs - which could be appended to the current proposal with minimal tweaking, but sets it up instead as a competitor. It does not meaningfully reduce the "size" of the proposal, as the pattern syntax is just as large as in the current proposal. [15:29:29.0561] I do like your emphasis on making the pattern-matching part an extractable bit of syntax that we can apply elsewhere - that's very cool. I think that can be done within the current proposal, and suggest pursuing that. [15:30:29.0180] However, the specifics of your proposal are flawed in ways we already encountered and fixed; once they're corrected, your proposal will end up more or less identical to the current proposal, + the new let-when/etc constructs. [15:31:04.0245] yulia: ^^^ [15:33:21.0517] (Note that we've recently added the ability to use plain functions as custom matchers that pass-thru the value unchanged, so `when(${isSomething}): ...;` works exactly as expected if `isSomething` is a boolean function; no need for the author to do the custom-matcher hoop-jumping we'd shown off in previous meetings. I think the README still needs updating in this regard, which is on me.) [15:57:52.0990] I think even if we changed nothing about the feature though, framing things in layers helps for digestibility such that champions, committee, and community alike can clearly see the value in each piece of the proposal [15:59:23.0624] like, the Friday before plenary, I ended up feeling like "actually I don't really find much objectionable here at all" but that was a surprise to me because it took careful re-examination of all the parts of the proposal while repeatedly asking myself "can we jettison this?" [16:57:11.0625] Oh sure, that's always true. And if we do want to accommodate let-when in the future, it might make sense to rearrange somewhat to talk just about the pattern syntax before we talked about the uses of it (match, other future things). [16:58:02.0104] It does just keep boggling me that our proposal is more or less equivalent to the first three other pattern-matching features I could think of first but something about how we present it, I guess, keeps making people think it's uber-complicated. 2022-07-27 [17:09:19.0344] (Or accommodate let-when now; it looks cool and the semantics seem pretty trivial (just don't leak bindings out of the pattern). It would mean a bigger feature, by definition, but if it helps motivate the layering to make the feature *feel* like it's made of smaller bits, that's fine with me.) [17:42:40.0770] > <@tabatkins:matrix.org> It does just keep boggling me that our proposal is more or less equivalent to the first three other pattern-matching features I could think of first but something about how we present it, I guess, keeps making people think it's uber-complicated. to be totally clear, I don't think it's complicated _as a pattern-matching construct_, I think the concern is with incorporating it into one's understanding of JS as a language [17:42:55.0887] it would very much be _worse_ if it were in strong conflict with prior art [17:44:24.0192] I guess? I don't see why JS is special here to have patterns be a foreign concept - every other language in our prior art list is more or less equivalent to JS (in the ways that are relevant here) and incorporate patterns (generally with extremely similar pattern syntaxes) just fine. [17:44:50.0597] I mean, if people believe that other language's pattern-matching features are *also* overly-complex, then fair cop I guess, but I haven't been given that impression yet. [17:45:55.0606] Reading between the lines of Yulia's proposal, it seems her objection is that the pattern syntax is special-cased to just the match expression, when it could be useful elsewhere - and that's some *very* reasonable feedback I'd like to incorporate, as I outlined above. [17:46:43.0714] tbh my biggest qualm was the pin operator and that's gone now so [17:46:57.0679] Allowing a destructuring pattern to be followed by a `when(...)`, incorporating the pattern syntax into the destructuring syntax, sounds great and useful. [17:47:07.0400] I think `${}` is nicely javascripty [17:49:00.0745] And allowing `when()` to *be* a destructuring pattern on its own, to establish the bindings at that level (usable, then, as `let when(...) = x`, or in for-of, or in arglists) makes *even more* sense, since it's actually a new feature (`let x when(...) = x` is syntax sugar for `let x = match(x) { when(...): x; default: {} };` - I think it justifies itself on the saved syntax weight, but it is just sugar). [17:51:54.0906] (All this taken straight from Yulia's proposal, just with the assumption that we use the existing proposal's pattern syntax since it avoids the issues I pointed out.) [18:09:44.0483] I have a few issues with the `let when` syntax, though I wrote that up in the gist I posted earlier. While I like the idea of composability, I find the specific syntax presented to be confusing in places as it doesn't align with existing JS syntax. [18:15:52.0368] > <@tabatkins:matrix.org> And allowing `when()` to *be* a destructuring pattern on its own, to establish the bindings at that level (usable, then, as `let when(...) = x`, or in for-of, or in arglists) makes *even more* sense, since it's actually a new feature (`let x when(...) = x` is syntax sugar for `let x = match(x) { when(...): x; default: {} };` - I think it justifies itself on the saved syntax weight, but it is just sugar). This desugaring is something I'm concerned about. It seems to work fine for `let x when... ` and `let { x } when...`, but doesn't work with `let [x] when...`. I'd much rather we throw an error in the `default:` case [00:16:35.0434] > <@tabatkins:matrix.org> (Note that we've recently added the ability to use plain functions as custom matchers that pass-thru the value unchanged, so `when(${isSomething}): ...;` works exactly as expected if `isSomething` is a boolean function; no need for the author to do the custom-matcher hoop-jumping we'd shown off in previous meetings. I think the README still needs updating in this regard, which is on me.) Not currently. Currently, `isSomething` need to have a `@@matcher` function, and that function can be a bool-returning function [00:38:48.0929] > <@tabatkins:matrix.org> However, the specifics of your proposal are flawed in ways we already encountered and fixed; once they're corrected, your proposal will end up more or less identical to the current proposal, + the new let-when/etc constructs. the goal was to more or less end up with what you had in the end, so this was intentional. It is a bit buried in there, but i acknowledge that I am missing some of the detail that your original proposal had, i was largely using short hand to try and get this out quickly [00:40:48.0100] > <@tabatkins:matrix.org> Reading between the lines of Yulia's proposal, it seems her objection is that the pattern syntax is special-cased to just the match expression, when it could be useful elsewhere - and that's some *very* reasonable feedback I'd like to incorporate, as I outlined above. I wouldn't put it as strongly as "objection" -- but yes this is an important point. Due to rushing this, two parts got intertwined and a bit confusing. 1) we need to layer this, how can we do that (so i started working on something, then 2) oh, if we layer it, some interesting stuff falls out like allowing pattern matching syntax in many places (im particularly interested in LHS) [00:41:05.0010] the layering concern was the driving concern. I would consider it the underlying objection if we want to use that term. [00:41:38.0327] i think there are a few interesting bits in there, I admit that as I was writing it I was thinking "i know we did that for a reason but i can't remember why right now, lets just get the basic ideas on paper" [00:42:19.0012] this is not a fully concrete proposal the way that yours is, fwiw [00:44:00.0598] * the layering concern was the driving concern. I would consider it the underlying objection if we want to use that term. [00:46:22.0006] * the layering concern was the driving concern. I would consider it the underlying objection if we want to use that term. The second part, I tried to do a little to much with the decomposition into constituent parts exposing smaller pieces, eventually building up the final proposal from that you have (as close as possible via layering) and also addressing a syntax concern which is that destructuring is already confusing for developers, this will make it significantly more so. We should tread carefully there and reconsider what syntax we are introducing. I think this is fully solvable. In fact, if we fully restrict the when statement to what is already recognizable, i would be amenable to that. That said, if we do that, we lose some of the interesting things that the proposal can do, and this part of the decomposition of it [00:46:49.0549] * the layering concern was the driving concern. I would consider it the underlying objection if we want to use that term. The second part, I tried to do a little to much with the decomposition into constituent parts exposing smaller pieces, eventually building up the final proposal from that you have (as close as possible via layering) and also addressing a syntax concern which is that destructuring is already confusing for developers, this will make it significantly more so. We should tread carefully there and reconsider what syntax we are introducing. I think this is fully solvable. In fact, if we fully restrict the when statement to what is already recognizable, i would be amenable to that. That said, if we do that, we lose some of the interesting things that the proposal can do, and this part of the decomposition of it. Something we can talk about. The syntax i was using was not final, i want to emphasize again [00:48:32.0570] for it being surprising: i too was surprised that we were trying for stage 2 so soon after presenting it just a month ago when I said I had concerns. But, as I mentioned, I was also sick so this isn't anyones fault [00:52:45.0502] it is also why i stepped down. But maybe we should have had better communication among the champions? I understand that tab and jordan are the most active, and also that we are spread rather equally around the world. However we shouldn't be getting into this situation if we are all considered champions. I don't know when the last pattern matching meeting even happened, or where it was decided that this goes to stage 2. This might be due to the fact that matrix loses messages, I don't know. In this case, especially given that I raised concerns just one month ago, explicit consent should have been sought among champions before trying to move it to stage 2. [00:53:39.0206] it was a really really shitty realization to see this on friday and think "oh, this isn't ready in my opinion, but its being done with my name right on the title slide and _i had no idea of any of the contents_" [00:54:36.0755] * it was a really really shitty realization to see this on friday and think "oh, this isn't ready in my opinion, but its being done with my name right on the title slide and _i had no idea of any of the contents_" [01:05:55.0900] We presented the current version at March, "it is feature ready and try to move to stage 2" [01:06:41.0855] Then it was blocked by Waldemar because we don't have a single line of spec text [01:07:48.0593] Then I started to working on spec text and completed it in June, Jordan refine the spec text and rewrite the sub-class matching part [01:18:29.0806] hm, then my time line is off. I raised the concern about layering right after the last presentation [01:19:58.0189] it felt like things happened very quickly and I didn't have the time to fully express my concerns. That is likely on me thouhg [01:23:27.0581] actually in march i had another major sick leave. [01:24:44.0119] That's unfortunate [01:25:14.0451] eh it hasn't been the best year, but then it probably makes more sense for me not to champion. As i mentioned -- i felt very uncomfortable when i realized that i was so out of sync [01:26:16.0030] You can rejoin now, we're slowing down to reconsider your concern [01:27:54.0931] Lets have a call about what i raised, I agree with tab that i missed a lot of the details from the main proposals, and there may be other ways to structure this [01:28:23.0131] but i do like the possibility of opening up pattern matching to other locations in the language [01:29:25.0882] i think it could be really interesting 2022-07-28 [03:07:45.0053] I hope you don't mind me butting in - just a distant observer. I quite like the presentation of the layering from yulia . However, one concern, which I think TabAtkins picked upon, with the specific content of the proposal is that the `let when` matching doesn't allow for matching on nested properties, which I think its pretty core the value of the original pattern matching proposal and is a common feature across prior art in other languages. For some context, I originally suggested expanding the matcher proposal to include implementation for at least all scalars in the language, and I provide some example use cases in the first few comments https://github.com/tc39/proposal-pattern-matching/issues/209 [03:09:40.0670] but I can definitely see the value of building up the feature from smaller parts that can be used standalone - it could make it a better integrated feature in the language if its made up of smaller parts that have value elsewhere [03:09:54.0895] easier to understand, and educate users [03:10:39.0292] let-when (in my original thinking) is intentionally restricted to make sure that it is unambiguous for users [03:10:55.0055] but, if you need more complex matching, you can break it into let ... when ... where the when is more powerful [03:11:49.0319] its an idea -- largely coming from a difficulty that you also noted in your examples that it is a little difficult to tell how destructuring works exactly. That problem will be compounded if we are not careful. Especially if we allow patterns in other contexts (which i believe will be very powerful and we should try) [03:12:35.0309] can you point me to a more specific example? I am (as always) low on time, so i can only take a glance at the very detailed comments you have. You can take one specific one and ill take a look [03:12:41.0808] yeah, but in prior art I wouldn't need to say use a `where` clause for matching against a nested `Some` for example [03:12:58.0842] A `Some` containing a `Some` [03:13:25.0257] can you write out what you have in mind? [03:13:48.0322] forgive the made up syntax, just copying it from the issue I linked ```ts type User = | { id: string, name: string} | { id: string, name: { name: string } } | string | number | null declare function getNameFromId(id: number): string; declare const user: User; // again I'm not sure about how binding works in this proposals, so taking a haphazard guess const str = match( user ) { when ( { name: _string } ) { name } when ( { name: { name } ) { name } when _string { user } when _number { getNameFromId(user) } otherwise { "unknown user" } } ``` [03:14:35.0120] its kinda arbitrary and refers back to when `do` was part of the proposal [03:14:41.0947] * its kinda arbitrary and refers back to when `do` was part of the proposal [03:14:56.0642] the proposal hasnt' changed at the moment [03:15:06.0904] so you mean this kind of match [03:15:10.0071] ```js const str = match( user ) { when ( { name: _string } ) { name } when ( { name: { name } ) { name } when _string { user } when _number { getNameFromId(user) } otherwise { "unknown user" } } ``` [03:15:30.0306] haha, yes [03:15:37.0802] I'm guessing you read the issue [03:15:41.0119] and i guess with the current proposal it would look like this [03:15:57.0669] `_string` is kinda like `${String}` or in a previous iteration `^String` [03:16:38.0065] ```js const str = match( user ) { when ( { name: ${String} } ) { name } when ( { name: { name } ) { name } when (${String}) { user } when ($number}) { getNameFromId(user) } otherwise { "unknown user" } } ``` [03:17:04.0729] yep, that is how it would roughly look I think in the current proposal [03:19:50.0377] I'm matching potentially on a union of possible values, and some of those values have nested properties that I want to match against [03:20:30.0944] in the fully layered version, we could have: ```js function hasName(user) { if (user.name) { if (user.name.name) { return MatchPattern({ result: true, value: user.name.name}) } return MatchPattern({ result: true, value: user.name}) } return MatchPattern({ result: false} ) } const str = match( user ) { let { name } when hasName { name } when String { user } when Number { getNameFromId(user) } otherwise { "unknown user" } } ``` [03:21:08.0821] I suppose the issue is then that I have to now create a dedicated function for matching against this - I suppose this could be solved in userland [03:21:26.0070] I can imagine that userland libraries will emerge that would allow you to construct that function [03:21:43.0444] but you could arguably do ```js const str = match( user ) { let when ( { name: { name } ) { name } let when ( { name } ) { name } when (${String}) { user } when ($number}) { getNameFromId(user) } otherwise { "unknown user" } } ``` this misses the string because i haven't thought that through [03:23:03.0296] > <@yulia:mozilla.org> in the fully layered version, we could have: > > > ```js > function hasName(user) { > if (user.name) { > if (user.name.name) { > return MatchPattern({ result: true, value: user.name.name}) > } > return MatchPattern({ result: true, value: user.name}) > } > return MatchPattern({ result: false} ) > } > const str = match( user ) { > let { name } when hasName { name } > when String { user } > when Number { getNameFromId(user) } > otherwise { "unknown user" } > } > ``` I think a consequence of this is that we will likely have a proliferation of user lands libraries that give you builders for these functions to build up complex patterns with nested values [03:23:04.0633] though this is also possible: ```js const str = match( user ) { let { name: name } when ( { name: { name: ${String} } ) { name } let { name } when ( { name: ${String} } ) { name } when (${String}) { user } when ($number}) { getNameFromId(user) } otherwise { "unknown user" } } ``` [03:23:11.0208] which might not be a bad thing [03:23:36.0402] it might give us more type safety in a dynamic language, which i guess is also an interest of mine [03:23:37.0914] oh that is interesting [03:23:50.0255] effectively matching on structures, and making that something that can be used almost anywhere [03:24:12.0014] but with the way the syntax is right now, it shadows and overlaps with destructuring in a way that may be ambiguous [03:24:27.0243] so in a way we are moving the pattern matching syntax slightly over to the right? [03:24:39.0215] or the left, it can be `when { ... } as { ... } ` [03:24:40.0388] `( { name: { name: ${String} } )` is going to create a funciton? [03:24:57.0437] equivalent to `hasName` [03:24:59.0641] > <@m-rutter:matrix.org> `( { name: { name: ${String} } )` is going to create a funciton? a matcher rather, but same idea [03:25:08.0751] matcher as defined in the current proposal [03:25:21.0122] ok, that sound fairly reasonable to me [03:25:35.0801] that could be a way of addressing my concern [03:26:16.0459] binding happens on the left, matcher is constructed in the middle, expression on the right [03:26:54.0366] i have a crazy idea (please no one shoot me for this, this is a thought experiment) ```js function handle(res when { status: 200 }) {...} // matched when no "body" present function handle(res when { status: 200, body: $validBody }) {...} // the match when "body" is present and valid function handle(res when { status: 200, body }) {...} // the match when "body" is present function handle(res when { status: 200 }) {...} // this overwrites the first function function handle(res) { ...} // fall through case ``` [03:27:09.0133] you might recognize this as pattern matching of a different sort -- specifically haskell-likes [03:27:29.0982] * i have a crazy idea (please no one shoot me for this, this is a thought experiment) ```js function handle(res when { status: 200 }) {...} // matched when no "body" present function handle(res when { status: 200, body: $validBody }) {...} // the match when "body" is present and valid function handle(res when { status: 200, body }) {...} // the match when "body" is present function handle(res when { status: 200 }) {...} // this overwrites the first function function handle(res) { ...} // fall through case ``` [03:27:32.0237] typescript would like to have a word, you just broke overloads :P [03:27:47.0377] i already spoke with them a bit about this ;) they don't hate it [03:27:48.0907] yet [03:28:12.0169] oh, they all have bodies [03:28:22.0017] typescript probably doesn't mind so much then [03:28:31.0244] but effectively, if you leave out the default case -- the engine would treat values passed to `handle` that don't match, as a call to an undefined function [03:28:36.0392] this reminds me of axum in rust [03:28:43.0264] with their extractor functions [03:28:55.0320] meaning that you could be restrictive in what kind of shapes can be passed to functions. i think this might be interesting. [03:29:40.0955] what i find additionally interesting is that WASM has roughly settled on a structural typing approach (with some nominal typing). This would give us a way to talk about what structures are permitted, and that would mean that we take a somewhat unified approach across the platform [03:29:47.0230] that said, its very fuzzy [03:29:54.0949] just, you know, interesting [03:30:01.0827] its an interesting idea [03:30:08.0551] don't immediately hate it [03:30:23.0175] but it might be a bit verbose [03:30:41.0348] and these are function statements, not expressions [03:30:59.0862] so I would probably need to define these statements in module scope [03:31:05.0785] * so I would probably need to define these statements in module scope [03:31:12.0475] yep, someone i spoke to suggested that we add sugar for it, ie: ```js let foo = (res when { // whaever } ) => { ... } | (res when { // whaever } ) => { ... }; ``` [03:31:43.0512] i mean, that is just the original proposal isn't it? [03:32:12.0806] it is close, with the exception that it gets interesting for single definitions [03:32:36.0158] so if its a single def, and you call it [03:32:38.0414] would it throw? [03:32:50.0270] yes, if you call it with a non matching shape, the function is treated as undefined [03:33:01.0507] basically baking in assertions and runtime type checking into the langauge [03:33:15.0830] my naive implementation would be: for each shaped function, we create an internal function that is only called for that shape [03:33:55.0472] this is currently fantasy, but interested to hear feedback. I think patterns everywhere is just so damn interesting [03:34:16.0516] I could imagine someone adding an eslint rule that says all functions need to be pattern functions [03:34:33.0674] so that at runtime every function call gets checked [03:34:52.0420] this may have an impact on initial runs of a script. That is -- this is effectively an if statement [03:35:11.0063] jits remove those, so on hot code it might be equally fast. slow path code may be slower -- something to consider [03:35:14.0631] * jits remove those, so on hot code it might be equally fast. slow path code may be slower -- something to consider [03:37:57.0323] * I hope you don't mind me butting in - just a distant observer. I quite like the presentation of the layering from yulia . However, one concern, which I think TabAtkins picked upon, with the specific content of the proposal is that the `let when` matching doesn't allow for matching on nested properties, which I think is pretty core the value of the original pattern matching proposal and is a common feature across prior art in other languages. For some context, I originally suggested expanding the matcher proposal to include implementation for at least all scalars in the language, and I provide some example use cases in the first few comments https://github.com/tc39/proposal-pattern-matching/issues/209 [03:39:16.0418] maybe making matcher and pattern construction syntax for custom matchers as standalone proposal could be the launch of some interesting paths [03:39:26.0132] my thoughts exactly ;) [03:39:37.0840] `match` could come later [03:40:15.0014] but you could then move on to match or extractor functions - made up a name for them [03:40:27.0196] * but you could then move on to match or extractor functions - made up a name for them [03:41:11.0257] for what its worth -- all of the syntax i proposed is entirely up in the air. it is just to get the idea across and express why we don't want to combine matchers with assignment [03:41:41.0787] or, restrict where they can be combined. or, maybe i am not creative enough, and they can be combined anyway -- someone might bring an interesting way to do it (personal reflection: i suspect it will still be difficult to read, and we want a split anyway for those cases) [03:41:53.0643] aliasing was a mistake tho [03:42:03.0172] (in destructuring, not in this proposal) [03:44:33.0599] * or, restrict where they can be combined. or, maybe i am not creative enough, and they can be combined anyway -- someone might bring an interesting way to do it (personal reflection: i suspect it will still be difficult to read, and we want a split anyway for those cases) [03:44:47.0742] * (in destructuring, not in this proposal) [03:45:54.0470] a downside I just thought of, what about `or`? [03:46:14.0274] actually, probably not an issue [03:46:28.0963] yep, or can be handled in various ways, as it represents a non-match [03:46:35.0394] but same behavior [03:47:05.0330] actually, say I have two patterns that are very different shapes [03:47:16.0730] but the same object matches both? [03:47:23.0428] the binding on the left, won't necessarily make sense [03:47:36.0864] I want both patterns to go into the same branch [03:47:43.0876] ah, i see [03:47:51.0991] that is a very common usecase [03:48:01.0979] at least in prior art [03:48:09.0475] is this for the match statement or the generic case? [03:48:24.0406] anything that involves binding I guess [03:48:44.0763] I was trying to think why most prior art marries binding and patterns [03:48:57.0016] i think largely due to repetition [03:49:50.0457] ```ts let { name: name } when ( { name: { name: ${String} } ) { name } let { name } when ( { name: ${String} } ) { name } ``` what if both of these cases are meant to do the same work in their branch [03:49:53.0083] and we have `do` [03:49:58.0586] and the body of `do` is complex [03:50:00.0254] but i would say the more important aspect -- is that it aids reading: a long statement is harder to comprehend than a short one, and if the two truely hold the same information then the repetition unnecessarily obfuscate what is happening [03:50:33.0246] > <@m-rutter:matrix.org> ```ts > let { name: name } when ( { name: { name: ${String} } ) { name } > let { name } when ( { name: ${String} } ) { name } > ``` > > what if both of these cases are meant to do the same work in their branch i think this example is why we want to be able to call functions that can return matchers [03:50:42.0560] rather than doing it in syntax [03:51:04.0659] syntax will always be restrictive -- what we enable to do easily in syntax should aid readability [03:51:28.0671] but the above would still be two statements in the current proposal iiuc [03:53:46.0549] _yulia is a big fan of the custom matchers, if it isn't obvious_ [03:54:05.0992] * and the body of `do` is complex and maybe makes use of values in the outer scope [03:56:51.0974] so we go back to the `hasName` matcher - so you can specify the pertinent value that you likely want to later bind [03:57:24.0274] * so we go back to the `hasName` matcher - so you can specify the pertainment value [03:57:46.0440] * so we go back to the `hasName` matcher - so you can specify the pertinent value [03:58:18.0694] which probably will result in userland libraries to aide in their construction - which might not be a bad thing [03:59:14.0991] * so we go back to the `hasName` matcher - so you can specify the pertinent value that you likely want to later bind [04:02:25.0430] yeah, maybe at some point this can be aided by syntax. but overall i am a big fan of functions as they allow what we cannot anticipate [04:02:35.0338] or perhaps, should not anticipate [06:42:48.0812] * I hope you don't mind me butting in - just a distant observer. I quite like the presentation of the layering from yulia . However, one concern, which I think TabAtkins picked upon, with the specific content of the proposal is that the `let when` matching doesn't allow for matching on nested properties, which I think is pretty core to the value of the original pattern matching proposal and is a common feature across prior art in other languages. For some context, I originally suggested expanding the matcher proposal to include implementation for at least all scalars in the language, and I provide some example use cases in the first few comments https://github.com/tc39/proposal-pattern-matching/issues/209 [08:25:16.0618] A "function returning a matcher" can just be a custom matcher that uses `match()` internally, I think? [08:25:32.0239] And that doesn't require us to reify the matcher structures into first-class values [08:28:51.0733] oh and Jack Works Yeah you're right, plain functions aren't usable. Need to write, like, \`when(${{\[Symbol.matcher\]: somePredicate}})`, which is a bit annoying. [08:29:57.0416] * oh and Jack Works Yeah you're right, plain functions aren't usable. Need to write, like, \`when(${{\[Symbol.matcher\]: somePredicate}})`, which is a bit annoying. [09:07:21.0459] > <@tabatkins:matrix.org> A "function returning a matcher" can just be a custom matcher that uses `match()` internally, I think? yep, also true. [09:11:39.0843] Hmmmmm I wonder if it's acceptable to have functions automatically act as custom matchers (if they don't have a Symbol.matcher on them or their prototype). It would mean if you wanted to check if the matchable *is* a particular function, you'll need to write a trivial `${x=>x===func}`, but I think I'm fine with that? And the fact that you can write arbitrarily-complex matchers with a tiny little arrow func like that seems *really* appealing. [09:13:08.0703] That would be putting us at enough carve-outs for non-primitive values that I'm becoming less sanguine about random non-primitives in `${}` patterns just being treated as an equality matcher. [09:14:07.0196] i'll open an issue [09:35:54.0180] https://github.com/tc39/proposal-pattern-matching/issues/282 2022-07-29 [22:12:08.0670] > <@yulia:mozilla.org> i have a crazy idea (please no one shoot me for this, this is a thought experiment) > > ```js > function handle(res when { status: 200 }) {...} // matched when no "body" present > > function handle(res when { status: 200, body: $validBody }) {...} // the match when "body" is present and valid > > function handle(res when { status: 200, body }) {...} // the match when "body" is present > > function handle(res when { status: 200 }) {...} // this overwrites the first function > > function handle(res) { ...} // fall through case > ``` not so bad [11:53:10.0812] I'm not sure functions should be privileged here, otherwise overload shenanigans might occur that might lead the ecosystem down a potentially bad path: ```js function f(...[x when Number, y when String]) { ... } function f(...[x when Number, y when Number]) { ... } function f(...[x when String, y when String]) { ... } // etc. ``` Not to mention what we would show for `f.toString()`, or how this would interact with parameter decorators, ... I'd much rather avoid introducing overload-like behavior for functions until long after both pattern matching, type annotations, and parameter decorators have shipped.