2024-04-04 [07:14:40.0599] i hope Temporal will be able to support the moon https://www.reuters.com/science/white-house-directs-nasa-create-time-standard-moon-2024-04-02/ [07:17:02.0068] That is a clear use case for sub-microsecond precision [07:21:24.0968] we'll need a v2 of Temporal where we specify the astronomical "home base" object. CLDR will need to be extended with a new catalog of astronomical objects and new lunar time zones. Should be doable! [07:22:01.0255] make sure you never end up with a home base inside a black hole [07:25:47.0132] maybe this time we can get it right and agree to use timex time (100 second minutes, 100 minute hours) on the moon [07:30:17.0337] * we'll need a v2 of Temporal where we specify the astronomical "home base" object for all operations. CLDR will need to be extended with a new catalog of astronomical objects and new lunar time zones. Should be doable! [12:23:57.0519] what I learned from researching the moon standard time in the past couple of days, is that local gravity affects the rate at which atomic clocks tick. Earth's atomic time is the average of several dozen atomic clocks, all with gravitational corrections applied so that they are effectively ticking at mean sea level (even if the clock itself is physically far above sea level) [12:34:03.0904] Yup, that's relativity for you. Gravity affects the rate time passes. [12:34:36.0407] yeah it's not just that it affects the rate at which the clocks tick, it's that it affects the rate at which _everything_ ticks https://en.wikipedia.org/wiki/Gravitational_time_dilation [12:35:20.0711] looking forward to Temporal being updated to include a "strength of local gravity" parameter :D [12:38:07.0627] right, so I'm a bit skeptical about whether it makes any sense to stretch the definition of POSIX time to incorporate other astronomical bodies [13:56:02.0800] gentle reminder to please add any and all plenary schedule constraints as soon as possible [14:45:50.0407] > <@bakkot:matrix.org> looking forward to Temporal being updated to include a "strength of local gravity" parameter :D "velocity relative to timebase station", too [14:46:24.0570] if we're doing velocity we need acceleration and jerk and so on as well, so we can properly represent dates in the future [14:46:37.0262] really just a full PDE I figure [14:52:33.0535] the curvature of the entire universe too [14:57:37.0525] what the fuck 2024-04-05 [17:19:11.0869] The current time systems in common use are only really designed to work on the surface of the Earth. Astronomers and other space scientists use different time systems which account for relativistic effects such as https://en.wikipedia.org/wiki/Barycentric_Dynamical_Time [17:21:00.0409] One of my favorite papers is measuring the masses of various solar system bodies such as Saturn using nothing more than clocks, comparing earthbound clocks to clocks outside the solar system (in the form of distant pulsars): https://arxiv.org/abs/1008.3607 [17:24:04.0707] The approach of counting seconds won't work well after we expand into space because clocks tick at different rates in different places in the solar system. If you want everyone to stay synced to Earth time, you'll need to adjust the duration of the second depending on where you are. If you want to keep the SI definition of a second, clocks in various places will diverge over time. [17:25:16.0778] We already have that issue with spacecraft, but the scientists and engineers working them account for the differences. [15:18:54.0730] if you'll all allow a brief single off-topic post: i'm baaaaaack. šŸ˜˜ [15:37:44.0040] requesting consensus for an eclipse break on Monday [15:39:42.0077] well. I guess that doesn't make much sense unless the break is like.. 2 hours long [15:39:44.0800] i wonder if people will be too distributed [15:39:46.0787] lol [15:39:50.0436] also that [15:40:40.0576] ah, actually for me the meeting will be over already [15:41:04.0653] yeah the farther east you are, i think 2024-04-06 [17:35:11.0123] I will be missing Monday for the eclipse, though I might try to dial in to observe the plenary for part of the day. [23:36:03.0603] https://github.com/tc39/Reflector/issues/522#issuecomment-2040991367 [23:38:46.0738] ā˜ļø draft schedule is available. all constraints have been accommodated 2024-04-08 [07:02:26.0932] is there a way to join the meeting without logging in [07:02:59.0511] I joined the call anonymously (no prior log in) [07:03:46.0202] you do not have to use the client [07:04:12.0479] oh i see, i can just enter any name/email, thanks [07:04:12.0879] if you click the "use on the web" it kind of implies you're signing in, but you just put your name and email and then join regardless of what you put in those boxes [07:04:22.0400] > <@shuyuguo:matrix.org> is there a way to join the meeting without logging in yes [07:04:37.0660] I just gave my name and email address, and then the "join as guest" button un-grayed [07:04:52.0415] (done on the native client) [07:07:19.0489] > <@bakkot:matrix.org> if you click the "use on the web" it kind of implies you're signing in, but you just put your name and email and then join regardless of what you put in those boxes thanks, that's what confused me [07:20:48.0227] Slides for this presentation at https://github.com/tc39/agendas/blob/littledan-patch-2/2024/tc39-2024-016.pdf (PR'd to the agenda also) [07:21:29.0352] ooohh we're bringing WinterCG into Ecma? [07:21:35.0040] yep! [07:21:40.0316] WinterTC [07:21:42.0628] I imagine it won't continue to be called WinterCG? [07:22:02.0902] More context: https://github.com/wintercg/admin/issues/58 [07:22:28.0541] The W3C CG will be the main place for technical development; WinterTC will be more for validation and formal standardization (this mirrors CycloneDX) [07:25:15.0098] Are temp checks an official vote? [07:25:21.0423] no [07:25:26.0632] It's weird that voting rules apply to temp checks [07:26:05.0675] yes, I agree. I think these Ecma rules only apply directly to official votes, which we do not have. That said, I'm not sure if IEs should be able to block. [07:26:09.0848] they're def not votes [07:26:35.0227] calling for consensus on advancement might be thought of as an implicit vote [07:26:39.0873] but temperature checks certainly aren't fotes [07:26:43.0137] * but temperature checks certainly aren't votes [07:26:44.0160] IEs definitely should not be voting/blocking [07:26:48.0579] IEs not being able to participate in consensus would be a radical change from the way tc39 has always operated, so that'd be something that needs discussion [07:27:02.0636] IEs should not be able to veto [07:27:08.0299] > <@ljharb:matrix.org> IEs not being able to participate in consensus would be a radical change from the way tc39 has always operated, so that'd be something that needs discussion OK, let's have this discussion now with Samina [07:27:13.0942] in the queue [07:27:55.0522] about temp checks sure. but ecma rules aren't really relevant; IEs participate in consensus here, and we'd need an agenda item to get consensus on changing that. [07:31:40.0124] shu: "being an IE" is at the discretion of chairs and ecma, so if that were ever abused it would be pretty easy to shut it down [07:32:48.0877] Does consensus for stage advancement count as a "vote" in ECMA terms? [07:33:08.0900] imo yes? [07:33:25.0901] I think so [07:33:26.0192] no [07:33:27.0989] like i don't see why there would be any bylaws if any TC can also just say "actually we don't do that"? [07:33:33.0498] it's consensus. not a ote. [07:33:34.0769] * it's consensus. not a vote. [07:33:43.0685] if it was a vote than we wouldn't ask individuals, we'd ask members. [07:33:50.0747] And we informally agree that the chairs only ask for the vote if there is would be full consensus [07:34:00.0218] i strongly disagree [07:34:04.0350] for example, google having 30 delegates would still only get 1 response to a call for consensus. that's not how we operate. [07:34:19.0368] there has long been disagreement within the committee about what the policy is. It's good for this to be on the table for discussion. [07:35:06.0618] * for example, google having 30 delegates would still only get 1 response to a call for consensus. that's not how we operate, or have literally ever operated. [07:35:28.0730] * And we informally agree that the champions only ask for the vote if there is would be full consensus [07:35:39.0534] * And we informally agree that the champions only "ask for the vote" if there is would be full consensus anyway, so no vote needed [07:35:45.0497] it's fine to discuss it, and if someone would like to, it'd be great to add an agenda item for it. but making a change here would be dramatic and unprecedented for tc39, and isn't a matter of simply "interpreting bylaws". [07:35:47.0564] It would be Really Good for execom to explicitly comment on our consensus practices. [07:36:13.0061] good reminder from @ljharb on GitHub teams, and thank you for your good work on setting up the GitHub teams. I do want to note that we have some inconsistencies between the GitHub and Ecma data (which member organizations have provided to Ecma and are published in the Ecma memento) and we will need to work through these. I believe neither one of these is perfectly up to date, and we will make a lot of progress by reconciling them. [07:36:30.0673] in general, laws serve the people, so if the bylaws conflict with what we actually do, the bylaws, not us, should change. [07:37:22.0954] > <@eemeli:mozilla.org> It would be Really Good for execom to explicitly comment on our consensus practices. In general, the ExeCom and Ecma management are very happy with TC39's "self-governance" and don't want to intervene too much. Ecma folks have previously said that they are happy with consensus-based processes like ours, but have found our way of dealing with vetos as absolute to be a bit much. [07:37:29.0948] * in general, laws serve the people, so if the bylaws conflict with what committees actually do, the bylaws, not us, should change. [07:39:42.0536] > <@ljharb:matrix.org> in general, laws serve the people, so if the bylaws conflict with what committees actually do, the bylaws, not us, should change. Yes, I very strongly agree with you that we should be thoughtful about any change to TC39's practices and not just blindly apply rules/bylaws, but consider whether they need to be changed if they don't fit, and submit such changes to the Ecma GA for a vote [in practice, consensus there too]. That said, the committee has long contained multiple opinions about whether IEs can block, so it's not clear what the change would/should be. Ecma rules are still set by the GA and not by TC39 precedent. [07:40:06.0634] I mean, this is why I got involved in Ecma stuff in the first place, to ensure that we had an IE policy in the first place and that we didn't accidentally kick Babel etc out [07:42:21.0294] littledan: Does "a bit much" mean that there's an expectation for us to change something at some point, or that the divergence from expectations is small enough to ignore? [07:43:36.0431] i mean sure, but opinions that differ from 100% of past history don't change what's actually happened [07:44:02.0808] it's fine to believe IEs shouldn't participate in consensus, for example, but that's only relevant in a discussion about whether to change the fact that they do :-) [07:45:10.0345] * it's fine to believe IEs shouldn't participate in consensus, for example, but that's only relevant in a discussion about whether to change the fact that they do :-) (which is also fine to have, ofc) [07:50:01.0990] > <@eemeli:mozilla.org> littledan: Does "a bit much" mean that there's an expectation for us to change something at some point, or that the divergence from expectations is small enough to ignore? No, there is no expectation that we change anything; they respect our self-management here and don't want to excessively intervene. [07:50:38.0182] > <@ljharb:matrix.org> i mean sure, but opinions that differ from 100% of past history don't change what's actually happened in past incidents of IEs blocking, IMO there is ambiguity as to whether the IE actually executed a block, or whether the IE expressed a strong negative opinion and the champion withdrew the request for advancement. [07:51:18.0335] there's no such thing as "block". there's either consensus for something, or not [07:51:19.0727] IEs are definitely here to participate actively in all technical discussions, and express strong negative opinions when there's a request for consensus, no doubt about that. If they make a good argument, they won't be the only blocker, so this is probably not a very impactful decision. [08:10:06.0150] 402 update slides: https://notes.igalia.com/p/fxx00_k5K#/ [08:14:39.0447] This transcriber wrote "camel cased" as CamelCased [08:14:47.0790] šŸ’Æ [08:19:43.0281] oh no, that's PascalCased tho [08:21:34.0343] ptomato: Could you please provide a link to the sovereign tech fund that is funding the test262 work? Or to your slides? [08:22:08.0509] it's Germany [08:22:14.0492] https://www.sovereigntechfund.de [08:22:27.0910] I was about to paste the content of the slide into the notes, will that work? [08:22:30.0491] they also gave nearly a million to the OpenJS Foundation last year [08:22:33.0853] otherwise yes what Jordan said [08:22:38.0930] they're doing what all governments should be doing :-) it's great [08:22:50.0560] Great, thank you :) [08:29:53.0178] Hypothetical question, would we take money from any sovereign tech fund, or would we consider things like human rights records, etc. before accepting funding? [08:29:55.0536] apparently there's also https://nlnet.nl/bluehatsprize/2024 currently seeking nominations [08:31:06.0719] > <@dminor:mozilla.org> Hypothetical question, would we take money from any sovereign tech fund, or would we consider things like human rights records, etc. before accepting funding? we = TC39? [08:33:33.0268] Well, in that case I guess it's Igalia's decision, but yes, I'm kind of wondering how we as a group would feel about this. [08:33:46.0813] There's also https://www.opentech.fund (from the US govt) [08:34:09.0282] > <@dminor:mozilla.org> Well, in that case I guess it's Igalia's decision, but yes, I'm kind of wondering how we as a group would feel about this. I guess this would be something at the Ecma level? In which case it'd definitely be a matter up for GA discussion [08:34:18.0521] i feel like TC39 itself isn't a legal entity to receive funds, so i imagine it's just up to Ecma [08:35:14.0197] * There's also https://www.opentech.fund (from the US govt) , specifically https://apply.opentech.fund/foss-sustainability-fund/ [08:35:30.0172] it'd be nice if the CoC update included what actions were taken (if any) instead of just saying "it's been resolved" [08:36:04.0448] the usual non-profit donation tricks abound, i imagine, with whether you can or cannot earmark donations for specific activities, especially if it's from a government [08:42:48.0319] > <@shuyuguo:matrix.org> the usual non-profit donation tricks abound, i imagine, with whether you can or cannot earmark donations for specific activities, especially if it's from a government FWIW Ecma is a Swiss "association", not a US 501(c)(3). I think this makes certain things more flexible. [08:42:49.0122] serious question: is there anyone who goes to the Firefox download page, says "300MB?! No thanks.", and leaves? [08:43:01.0054] I think by the time you're there, you're downloading Firefox, right? [08:43:11.0121] it's sort of a cumulative thing though, right? [08:43:19.0205] especially for mobile [08:43:21.0208] my intuition on almost all size concerns are about mobile, not desktop [08:43:25.0833] you mean Mozilla wants to save upload bandwidth? [08:43:33.0612] indeed i don't think people say that to the desktop downloader [08:43:37.0711] > <@michaelficarra:matrix.org> serious question: is there anyone who goes to the Firefox download page, says "300MB?! No thanks.", and leaves? When your phone is out of storage you have to start going through the list of big apps šŸ¤· [08:43:52.0500] And all phones already have a built-in browser [08:44:03.0169] > <@shuyuguo:matrix.org> the usual non-profit donation tricks abound, i imagine, with whether you can or cannot earmark donations for specific activities, especially if it's from a government * FWIW Ecma is a Swiss "association", not a US 501(c)(3). I think this makes certain things more flexible (IANAL!) [08:44:24.0422] also, markets with significantly cheaper hardware [08:44:35.0991] how small is the average phone in those markets these days? [08:44:52.0859] I think it's more about bandwidth [08:45:08.0499] > <@ljharb:matrix.org> how small is the average phone in those markets these days? Idk but my dad is always uninstalling apps to free up space because his phone is full [08:45:18.0199] * I think it's more about download bandwidth [08:48:05.0841] > <@ljharb:matrix.org> how small is the average phone in those markets these days? (nominally 64GB-128GB, usually) [08:48:18.0998] that's what i'd expect [08:48:47.0927] for me it's images/movies, not apps, that take up space [08:49:31.0939] > <@michaelficarra:matrix.org> serious question: is there anyone who goes to the Firefox download page, says "300MB?! No thanks.", and leaves? To be picky, Firefox desktop on MacOS is currently 134Mb, and on Android it's 88Mb. [08:49:44.0151] > <@michaelficarra:matrix.org> serious question: is there anyone who goes to the Firefox download page, says "300MB?! No thanks.", and leaves? * To be picky, Firefox desktop on MacOS is currently 134MB, and on Android it's 88MB. [08:49:57.0772] yeah I was too lazy to look up the actual number [08:50:25.0530] here, let me fix it for you: "134MB?! No thanks." [08:51:12.0437] > <@jesse:igalia.com> for me it's images/movies, not apps, that take up space spotify's cache grows nearly exponentially [08:53:42.0877] > <@michaelficarra:matrix.org> it'd be nice if the CoC update included what actions were taken (if any) instead of just saying "it's been resolved" per the Code of Conduct: > The Committee will never publicly discuss the issueĶ¾ all public statements, if needed, will be made by the TC39 Chair and/or the Ecma Secretariat. [08:55:37.0682] telling us what actions were taken (like somebody being banned from a particular forum) is not discussing the issue IMO [08:58:53.0469] The CoC committee could refer more public statements to the chair or secretariat for disclosure. Overall I think we have enough experience that statements like, "there was a report, it was handled" don't really make all of TC39 feel more secure about the process. But maybe we should just trust the CoC committee, and even these notifications are unnecessary--confidentiality is really important to preserve. [08:59:21.0144] yeah i think if you want to be confidential just don't update [08:59:31.0979] 100% confidential, that is [09:00:38.0215] depending on the action taken, saying what that action was may have privacy risk [09:00:45.0184] there's no harm in evolving our CoC processes in order to better serve delegates and make everyone feel more secure about our working environment here [09:10:07.0761] it's up to TC39 what the CoC is and how the CoC committee reports back to TC39 and the ExeCom/GA. if folks want to change the CoC, that's fine. it needs to be raised and get consensus at plenary, and I think Ecma ExeCom also would need to support if I'm not mistaken [09:11:13.0487] * it's up to TC39 what the CoC is and how the CoC committee reports back to TC39 and the ExeCom/GA. if folks want to change the CoC, that's fine. it needs to be raised and get consensus at plenary [09:22:37.0997] Definitely needs an agenda item to draw a conclusion; this is just some earlier discussion/feedback, which is also valid (like our above discussion about Ecma rules despite this not being a GA meeting). The CoC committee could also be the ones to propose something to plenary, based on this kind of feedback. [09:33:14.0276] you're absolutely right. I want to be clear it's not a matter the CoC committee can unilaterally decide [09:40:03.0157] About blocks: IMO IEs should generally be able to operate equally to all other committee delegates, and the process focus should probably be more on, what can we do in response to someone (anyone) blocking, and whether the committee may still have general consensus (e.g., by establishing that general consensus at a follow-on meeting once we all have more time to consider things). [10:06:38.0293] I don't know that "I would prefer to work with arrays" is... really an answer to "what is the benefit"? [10:13:14.0896] yeah I think we kinda settled this with iterator helpers a while ago... I did try to give more space to the iterables vs iterators discussion with inviting Axel in, etc. [10:14:40.0695] i mean i also to prefer to work with arrays when cache locality matters [10:14:53.0769] but i don't know if i care about it for joint iteration in particular [10:15:14.0243] and "caring about cache locality" certainly argues for case-by-case for the operation, not a catch-all [10:15:26.0619] to be clear i'm not arguing for 100% symmetry [10:16:32.0099] FYI I consider take/drop to be the analogue to slice @nicolo-ribaudo:matrix.org @bakkot:matrix.org [10:16:45.0068] they're certainly similar, just not identical [10:20:36.0850] I would like us to explicitly have a policy that we don't use "this package has many downloads" as a reason to add a feature [10:20:59.0442] "this package has many dependents from different authors", sure [10:21:41.0686] (in this case, the package has a dependency of Jest) [10:21:45.0127] yeah i was more saying, this is empirical evidence that the original belief is false [10:21:54.0495] (that everybody would prefer an aiife) [10:22:13.0426] * (in this case, the package is a transitive dependency of Jest) [10:22:30.0550] > <@shuyuguo:matrix.org> but i don't know if i care about it for joint iteration in particular please add these comments to https://github.com/tc39/proposal-joint-iteration/issues/1 [10:23:32.0294] * (in this case, the package is a transitive dependency of Jest and yargs) [10:26:24.0038] ok [10:26:28.0184] tbc, the closest analogy is `new Promise(r => r(foo, ...args))`, not a try-catch [10:26:41.0794] * tbc, the closest analogy is `new Promise(r => r(foo(args)))`, not a try-catch [10:26:48.0086] We've heard a lot of skepticism on motivation. Maybe we should do a temperature check/ [10:26:52.0606] * We've heard a lot of skepticism on motivation. Maybe we should do a temperature check? [10:27:56.0365] My position is exactly the same [10:28:03.0225] "Not bad, but not convinced that it's useful" [10:29:54.0512] Promise.prototype.catch/finally are really different from Promise.try... [10:30:26.0888] we have spent more time talking about the check than just doing the check [10:31:17.0288] I like the hoisting of a sometimes promised function into a definite promise. It helps decluttering functional chaining. [10:31:59.0561] > <@shuyuguo:matrix.org> we have spent more time talking about the check than just doing the check we do that every time. but temperature checks are rare. if they were more common, I think we wouldn't have to talk about them so much [10:34:06.0303] > <@littledan:matrix.org> Promise.prototype.catch/finally are really different from Promise.try... could you say more about what the big differences are? [10:34:52.0945] Temp polls should have a symmetrical state, going from Strong Positive to Strong Negative [10:36:06.0616] I voted "indifferent" based on agreeing with what Nicolo said. The intended meaning is not "abstain" [10:37:12.0071] "very weak negative" is how I intended my "indifferent" [10:37:23.0817] I assumed "šŸ‘€" was abstain [10:37:39.0733] Indifferent indicates that you had time to consider the issue and vote. [10:38:03.0075] Indifferent has no negative connotations to me. [10:38:28.0604] Without "indifferent" it would be hard to tell if people had a chance to vote. [10:38:33.0136] communication is all about relationships and context, and not only absolute meanings of words [10:39:14.0024] here, several people explicitly clarified that they specifically meant "very weak negative", so it doesn't really matter if "indifferent" generally has no negative connotations [10:39:22.0218] anyway it has Stage 2.7 so this is closed, I think [10:39:31.0287] I guess given that we would not advance a proposal unless at least some people were strongly positive, and that "very weak negative" is "I don't object to advancement", there's not much practical difference between the two [10:40:09.0667] in this case, yes [10:40:23.0112] more of an aside, but we regularly change the semantics of what the temp check options are because they don't always align well with the question [10:40:25.0642] I did want to elicit some more positive/strongly positive feedback, which we did get [10:42:13.0844] just a backslash wouldn't work anyway since we got rid of identity escapes [10:42:30.0766] so it'd be replacing the first character with an escape sequence for that character, like a hex escape [10:42:39.0129] if this is even needed, which I don't believe it is [10:44:33.0107] > <@nicolo-ribaudo:matrix.org> Temp polls should have a symmetrical state, going from Strong Positive to Strong Negative I think this choice was deliberate because it was trying to avoid negativity, but after a couple years of experience, it doesn't seem like that design worked. I like the idea of a basically 1-5 symmetrical scale; the other options don't seem to be very easy to interpret. [10:44:58.0284] and in particular, temperature checks were designed to avoid, well, this kind of vote as a use case, to be honest [10:44:58.0494] I opened https://github.com/bterlson/tcq/pull/67for now [10:45:12.0241] * I opened https://github.com/bterlson/tcq/pull/67 for now [10:46:35.0698] strictly more options?! [10:46:42.0933] please no, please get rid of some options at the same time [10:47:37.0849] Who should take the fall, "Strongly positive" or "Following" [10:47:59.0219] following [10:48:03.0667] following. has anyone ever voted following [10:48:31.0037] following for sure [10:49:18.0624] So "unconvinced" is weakly negative, and "negative" is strongly negative? Should we rename the options to be symmetrical? [10:49:24.0508] also confused because if you're confused, you should be asking for clarification, not voting! [10:49:44.0896] I guess in this case, "unconvinced" being the strongest negative option makes it scary to vote for [10:50:03.0976] no, confused is good, we should keep it--it's a big problem if people are confused and afraid to ask! [10:50:43.0771] I think calling it unconvinced is more expressive than "weakly negative" [10:51:04.0400] Or "This temperature check has been worded terribly I don't know what I'm voting" šŸ˜› [10:51:07.0151] OK, I'm convinced :) [10:51:15.0439] * Or "This temperature check has been worded terribly I don't understand what the options mean in this case" šŸ˜› [10:51:25.0428] the most important thing is to have something stronger than unconvinced, so that unconvinced becomes less harsh, I think [10:57:14.0993] Ughh I just noticed that I forgot to add slides to one of my topics on the agenda (a status update) [10:57:26.0116] this is enough change in the proposal that, even Wednesday, I don't think I'd be prepared to advance to 2.7 [10:57:26.0547] * Ughh I just noticed that I forgot to add slides to one of my topics on the agenda (a status update) -- I'm sorry [10:57:41.0666] 100% agreed [10:58:06.0556] maybe with just one of these changes, but with 2 or 3 i'd rather wait [10:58:50.0013] to be clear, I think they're all good improvements, but I'd want to review it as a whole again [11:00:31.0247] yep, i've unchecked all the signoffs so i know to re-ask [11:00:42.0656] btw for the steno, can we get them to stop double spacing after periods? [11:01:18.0285] they're also inserting newlines at the page width instead of letting it wrap šŸ˜­ [11:02:17.0713] "Can you please set your line width to 100_000_000?" [11:02:43.0350] both of those, yes please [11:03:15.0141] we've asked the stenographers to do this before. They should be doing it. I'll call them today. [11:03:27.0536] They've fixed this in the past, it's just that not all of them do it [11:25:27.0279] waldemar: On the topic of RegExp escapes, what escaping do you think we ought to use for null bytes? We have `\0`, but `\x00` is plenty readable, and `\0` has the complication that you can't use it if the next character is an ASCII digit. My inclination is to use `\x00`. [11:56:29.0626] should `microwait` be called `microWait`? [11:58:54.0669] should "kilogram" be spelled "kiloGram"? [12:00:52.0828] if it was a JS API? probably [12:01:03.0838] nooooo [12:01:14.0277] we use camel case when it's two words [12:01:21.0453] but "micro" is a prefix, not a new word [12:01:47.0696] `setTimeOut` [12:03:47.0535] Feedback about WebEx: much better than zoom [12:04:04.0285] i liked how it had a light mode [12:04:07.0488] down with dark mode!! [12:04:11.0315] much better than I expected, about the same as zoom [12:04:11.0484] please qualify better better. [12:04:11.0796] it is literally indistinguishable afacit [12:04:24.0283] In Zoom I always have audio issues [12:04:30.0244] It never works the first time [12:04:41.0866] is this a zoom problem or is this "year of linux on the desktop" problem [12:04:52.0340] video/screenshare entirely didn't work on firefox for me, so webex is a šŸ‘Žļø from me [12:05:03.0336] > <@shuyuguo:matrix.org> is this a zoom problem or is this "year of linux on the desktop" problem A year of firefox problem šŸ˜› [12:05:07.0476] it only allowed you to share "apps", not windows, so if I changed which Chrome window was focused, it would share that one instead [12:05:11.0874] IMO the web version was nicer than zoom and thus I had no reason to even attempt to use the native (apropos the parens thingie that was weird) and zoom's native version just takes years off my life [12:05:13.0098] > <@pchimento:igalia.com> video/screenshare entirely didn't work on firefox for me, so webex is a šŸ‘Žļø from me Oh weird, I had zero problems with it [12:05:25.0322] > <@usharma:igalia.com> IMO the web version was nicer than zoom and thus I had no reason to even attempt to use the native (apropos the parens thingie that was weird) and zoom's native version just takes years off my life Oh yes, I use the web version for both [12:05:31.0690] Long live the web [12:06:16.0136] > <@usharma:igalia.com> IMO the web version was nicer than zoom and thus I had no reason to even attempt to use the native (apropos the parens thingie that was weird) and zoom's native version just takes years off my life but I'd clarify that calling zoom not working hard enough on their linux app a linux problem is unfair [12:06:35.0866] like other native video conferencing apps exist, many of them are electron based and still do a vastly better job [13:17:37.0105] > <@michaelficarra:matrix.org> it only allowed you to share "apps", not windows, so if I changed which Chrome window was focused, it would share that one instead mine, from chrome, let me choose which specific chrome tab i wanted to share [13:17:41.0839] > <@michaelficarra:matrix.org> it only allowed you to share "apps", not windows, so if I changed which Chrome window was focused, it would share that one instead * mine, from chrome, let me choose which specific chrome tab i wanted to share (zoom does also) [13:30:12.0215] I think that's a chrome/chromium-specific feature [16:10:10.0622] > <@bakkot:matrix.org> waldemar: On the topic of RegExp escapes, what escaping do you think we ought to use for null bytes? We have `\0`, but `\x00` is plenty readable, and `\0` has the complication that you can't use it if the next character is an ASCII digit. My inclination is to use `\x00`. I didn't ask that question, but I did notice that non-whitespace control characters weren't escaped. I thought that was deliberate, but escaping them would be a worthwhile change for readability. I agree with `\x00` for the reasons you state. 2024-04-09 [06:52:32.0959] good morning/evening/night everyone! [06:52:36.0888] meeting starts in ~7 [07:29:19.0744] I have trouble understanding the motivation for this null change, but also it doesn't seem harmful [07:29:55.0863] (or rather, I have trouble understanding the motivation for the null skipping in the first place) [07:30:39.0976] Random fact: Babel accidentally ignores `null`s while guaranteeing at least one `await` -- we have a PR open to match the behavior of the current (pre-this-change) proposal [07:35:07.0570] It seems this PR try to avoid nulls introducing extra awaits, am I understanding this correctly? [07:35:17.0535] Yes [07:37:51.0235] this is a small thing, but i'd be interested in people's thoughts on banning `using` inside base switch cases: https://github.com/tc39/proposal-explicit-resource-management/issues/215#issuecomment-2040486653 [07:39:23.0609] Babel's `using` support in `switch` is currently completely broken [07:39:42.0017] Because everything in `switch` is incredibly annoying to compile [07:39:57.0272] i wanna ban it but recognize that'll break symmetry with let/const [07:40:52.0756] Is it for implementation reasons? [07:41:54.0326] it's laid out in the comment -- saves some codegen complexity [07:42:36.0729] we can unroll the dispose loop in all cases except switch cases [07:44:25.0497] I'm trying to think of any usecase for `using` in switch, but I cannot think of any where I would want `using`'s scope to fall through multiple cases [07:46:00.0809] (Babel doesn't unroll the loop anymore, so for us it wouldn't actually be difficult to fix `using` in `switch`) [07:46:39.0248] we don't unroll currently, but would like to [07:50:26.0532] > <@nicolo-ribaudo:matrix.org> I'm trying to think of any usecase for `using` in switch, but I cannot think of any where I would want `using`'s scope to fall through multiple cases The most relevant case I can see would be something like manual loop unrolling, where you skip over the `using` for a chunk of operations when it isn't needed, such as if a lock was needed for a 16k chunk of data that isn't needed for a smaller chunk of data. The point of manual loop unrolling is to avoid a bunch of comparisons and branches based on a single condition such as input size, so a smaller chunk of data skips over the `using` (and avoids the extraneous `null`/`undefined` check), while a larger chunk of data enforces the `using` and holds the lock until the current iteration of the loop ends. [07:52:52.0530] Oh I see, like for transpiling generators [07:53:13.0016] (in a world where generators are transpiled and `using` is not I guess) [07:53:51.0282] ```js for (let start = 0; start < len; start += 8) { switch (start % 8) { case 0: // full chunk, perform lock using lck = new UniqueLock(mut); readByte(); case 1: readByte(); case 2: readByte(); case 3: readByte(); case 4: readByte(); case 5: readByte(); case 6: readByte(); case 7: readByte(); } // lock released if taken } ``` [07:56:40.0590] not for transpiling generators, no. that would be using a `switch` as a finite state machine, but an FSM would result in the `using` terminating early. Loop unrolling in this case is to handle chunks of data without continuously spinning and checking in the `for` to process one element at a time and cutting down the number of comparisons/branching from n to 1/n for a given chunk size of n. [07:58:31.0396] * not for transpiling generators, no. that would be using a `switch` as a finite state machine, but an FSM would result in the `using` terminating early. Loop unrolling in this case is to handle chunks of data without continuously spinning and checking in the `for` to process one element at a time and cut down the number of comparisons/branching from n to 1/n for a given chunk size of n. [08:25:18.0882] If we could get to Ashley's point, he can explain why TLA running eagerly is useful within Bloomberg [08:26:40.0529] tla already can break expectations... [08:29:10.0363] certainly a TLA being added anywhere in your graph is a breaking change [08:29:20.0562] The thing is: in practice, you really shouldn't use JSON or CSS modules either, due to the lack of built-in bundling. The limited utility of built-in modules is not at all limited to this feature [08:29:46.0958] this strongly suggests to me that we need a path to standardize bundler-only syntax [08:29:48.0867] until then, we'll be talking about features that are mostly useful for bundlers and non-web environments [08:29:55.0338] * certainly a TLA being added anywhere in your graph (that's not an entrpoint) is a breaking change [08:30:13.0856] > <@bakkot:matrix.org> this strongly suggests to me that we need a path to standardize bundler-only syntax I disagree; I think we should fix the issues to allow native modules to be usable in browsers [08:30:27.0596] i disagree with that disagree [08:30:32.0747] doesn't seem a good use of resources tbh [08:31:06.0008] > <@littledan:matrix.org> I disagree; I think we should fix the issues to allow native modules to be usable in browsers That is also good! And then, after that, standardizing things like this everywhere instead of bundler-only. But not before that. [08:31:13.0688] these are all arguments against ES6 modules in the first place [08:31:22.0335] *cough* [08:31:31.0248] I mean, yes. [08:31:36.0224] But that ship has sailed. [08:31:45.0035] you're soooo close [08:31:53.0368] bundler-related, but could `import defer` potentially avoid fetch and parse as well if we could serialize the import graph on the server side ahead of time? [08:32:03.0172] In Jack's "assert sync" proposal I suggest we can also add "use async" directive which might help to make TLA explicit. Maybe it also help this issue. [08:32:16.0253] well, yeah, so this would be good input to the module harmony group, if we don't like modules features anymore... [08:32:19.0410] I think standardizing ES6 modules as bundler-only syntax would have been better than the current world [08:32:33.0030] or is that not an option because fetch is async [08:33:28.0283] > <@littledan:matrix.org> well, yeah, so this would be good input to the module harmony group, if we don't like modules features anymore... `import source` is useful on the web! [08:33:45.0613] so it's not "module features" in general, just those specific ones which aren't useful on the web [08:34:11.0898] /me Taking over Chair responsibilities for a bit [08:34:12.0251] (possibly "[...] aren't useful on the web _yet_") [08:34:29.0614] I think the Matrix results show that it's useful on the web *in practice* [08:34:53.0125] It's a good thing that bundlers are aligned to TC39 and browsers in their syntax and semantics. It will be good for us to preserve that. [08:35:07.0286] > <@rbuckton:matrix.org> bundler-related, but could `import defer` potentially avoid fetch and parse as well if we could serialize the import graph on the server side ahead of time? yep. That's what we do at Bloomberg. We find the TLA at build time. And add a simplified module graph of which imports have TLA deps to our equivalent of 'package.json' [08:35:09.0814] IIUC the Matrix results show that it's useful on the web to have bundler-level syntax for this, but not that it's useful to have in browsers, right? [08:35:22.0291] we've made tons of progress by actually building things into JS; the code splitting situation was a mess before import(), and it resulted in code splitting not occurring. [08:35:30.0794] I am not proposing "give up on this"; I am proposing "have a path to standardize bundler-level syntax that isn't actually in browsers" [08:35:40.0746] TC39 is the JavaScript standards committee--our role is to find this common syntax [08:36:27.0193] once we find common syntax, it's OK if some implementations fall behind and don't implement everything, but it'd be a big cost to split the language in two [08:36:33.0100] my response is the thing Shu is saying out loud right now [08:36:54.0555] we should have this as an actual agenda item; that'd make it easier to discuss [08:39:05.0549] IMO Stage 3 serves this purpose of "it's in tools and not necessarily in browsers". Maybe we just want to leave things in Stage 3 for longer. [08:39:36.0712] fwiw eslint refuses to implement features prior to stage 4, though that's maybe a them problem [08:41:07.0294] stage 3 is when it gets shipped in browsers tho [08:41:10.0342] * stage 3 is when it gets shipped in (most) browsers tho [08:42:48.0786] John-David Dalton: why is it disingenuous? this is arguably _better_ handled by tools. tools have a different view (they can see the whole app) and optimization opportunities than a VM [08:43:14.0638] we designed a super static module system. it is not surprising to me that it has been leveraged to success with ahead-of-time tooling [08:44:10.0049] > <@bakkot:matrix.org> fwiw eslint refuses to implement features prior to stage 4, though that's maybe a them problem well, there's babel-eslint [08:46:23.0828] (`@babel/eslint-parser` as of babel 7) [08:47:03.0198] > <@ljharb:matrix.org> stage 3 is when it gets shipped in (most) browsers tho IMO browsers *should* ship features like this at that point, but if they want to leave some features out of browsers for now (but we still assess that the feature is solid enough for Stage 3), maybe they can just let them sit in that bucket. Doesn't require holding back 2.7 or 3. [08:47:22.0696] > <@ljharb:matrix.org> stage 3 is when it gets shipped in (most) browsers tho * I'd be happiest if browsers ship features like this at that point, but if they want to leave some features out of browsers for now (but we still assess that the feature is solid enough for Stage 3), maybe they can just let them sit in that bucket. Doesn't require holding back 2.7 or 3. [08:47:33.0695] 3 is a pretty clear signal, that's the big purpose of 2.7. i agree it wouldn't require holding back 2.7 [08:48:23.0429] so is this feature going to encourage the dreaded "barrel" module pattern? [08:48:36.0398] > <@michaelficarra:matrix.org> so is this feature going to encourage the dreaded "barrel" module pattern? it's about optimizing the feature--no encouragement is needed [08:48:44.0114] While that's technically true, I would be happier revising our process if we want to do that - I would prefer that stage 2.7/3 be taken as a commitment from browsers to implement a feature at some point. I don't want to get to a point where browsers allow features to reach 2.7 that they intend to never implement. [08:49:04.0020] agree [08:49:09.0469] the two design spaces are honestly different [08:49:14.0285] the constraints are different [08:49:17.0420] > <@michaelficarra:matrix.org> so is this feature going to encourage the dreaded "barrel" module pattern? oof, i hadn't thought about that. [08:49:29.0454] > <@bakkot:matrix.org> While that's technically true, I would be happier revising our process if we want to do that - I would prefer that stage 2.7/3 be taken as a commitment from browsers to implement a feature at some point. I don't want to get to a point where browsers allow features to reach 2.7 that they intend to never implement. ah OK well I'm happy to get browser commitments like that; I don't want to discourage that. I haven't thought this through enough. [08:49:54.0372] > <@littledan:matrix.org> it's about optimizing the feature--no encouragement is needed well it doesn't provide any benefit to those not using the pattern, so it seems like encouragement to use it [08:50:06.0567] littledan: i have been thinking about for most of this year, i'll give you something more thought out soon [08:51:05.0282] > <@michaelficarra:matrix.org> well it doesn't provide any benefit to those not using the pattern, so it seems like encouragement to use it I think you're overthinking it... there's a lot of existing usages of this pattern, and it's slow, and it is hard to migrate away from (a lot of effort has gone into this migration) [08:51:20.0789] it's not about newly incentivizing usage [08:51:54.0139] so someone who wants to get similar tree-shakeability can just dump this pattern? [08:52:24.0635] > <@michaelficarra:matrix.org> so someone who wants to get similar tree-shakeability can just dump this pattern? sure, if you can get all your dependencies to dump this pattern, then you'll have no reason to use this pattern in your dependency tree [08:52:44.0025] > <@michaelficarra:matrix.org> so someone who wants to get similar tree-shakeability can just dump this pattern? * sure, if you can get all your dependencies to dump this pattern, then you'll have no reason to use this feature in your dependency tree [08:52:59.0639] there's a growing group of vocal folks in the ecosystem telling people to get rid of barrel modules, fwiw [08:53:18.0876] (coinbase's RN app's binary size dropped 71% when we banned barrel exports) [08:53:40.0499] * (coinbase's RN app's binary size dropped 71% when we/they banned barrel exports back in 2021) [08:53:52.0538] > <@ljharb:matrix.org> there's a growing group of vocal folks in the ecosystem telling people to get rid of barrel modules, fwiw yes, they're doing good work; I think those two groups (the ecosystem effort and this proposal) should be understood to be supporting each other [08:54:42.0621] even with import defer, barrel exports will still result in a larger app than "just import what you need directly", no? [08:54:53.0357] I don't think "this feature is not useful in browsers" is a new category of objection? [08:54:57.0161] that's like... the main objection [08:55:03.0284] can someone remind me why people ever did that in the first place? [08:55:23.0889] i don't know why people tend to like "god objects" [08:55:44.0526] * i don't know why people tend to like "god objects", deep imports is The Way [08:55:53.0557] * i don't know why people tend to like "god objects", deep imports is The Way. but they do tend to like them. [08:56:02.0865] maybe the pattern was copied from python modules and `__init__.py`? [08:56:08.0294] > <@michaelficarra:matrix.org> can someone remind me why people ever did that in the first place? I always understood it to be a response to named exports being a thing, making use of that syntactic space, whereas require returning a single function was more natural [08:56:16.0208] i've seen people have a visceral preference for one `import` keyword that has N identifiers, instead of N `import` keywords [08:56:44.0865] > <@bakkot:matrix.org> that's like... the main objection yeah i feel like most of our concerns that have caused feature compromises and redesigns boil down to this. sometimes it's couched in more specific terms, like "performance footgun" etc [08:56:59.0201] I'm finding it very hard to find sympathy for these people... [08:57:00.0961] also i think that a number of major IDEs implemented auto-refactoring features for named exports but ignored default exports [08:57:06.0477] * also i think that a number of major IDEs implemented auto-refactoring features for named exports but ignored default exports (for no technical reason) [08:57:15.0173] * also i think that a number of major IDEs implemented auto-refactoring features for named exports but ignored default exports (for no technical reason) which drove increased usage of named exports [08:57:26.0804] > <@michaelficarra:matrix.org> I'm finding it very hard to find sympathy for these people... why does it matter whose fault this is? it's currently a performance issue, and this is a solution. [08:58:01.0118] but is "don't use barrel files" perhaps a better solution? [08:58:08.0095] * but is "don't use barrel files" perhaps a better solution? (genuine question) [08:58:53.0606] Barrels are so convenient, and so awful for performance (both browsers and bundlers) [08:58:56.0043] I dunno, there's a lot of recommendations about performance that people aren't taking up... [08:59:24.0498] @guybedford:matrix.org Your mic is echoing [08:59:47.0003] but wouldn't this be a recommendation for performance in exactly the same way? it requires work to adopt; it doesn't just give you performance for free [08:59:59.0606] sorry guybedford bad timing [09:00:06.0237] Timebox! [09:00:09.0827] and if the people in question were willing to do work to get performance they already can already do so, right? [09:01:13.0988] @nicolo-ribaudo:matrix.org your async feedback from me is that, for now, I'm unconvinced by the motivation [09:01:30.0332] > <@jridgewell:matrix.org> Barrels are so convenient, and so awful for performance (both browsers and bundlers) convenience often makes for better paving than good intentions [09:01:48.0748] > <@bakkot:matrix.org> but wouldn't this be a recommendation for performance in exactly the same way? it requires work to adopt; it doesn't just give you performance for free The uptake is a "semver-minor" change--it doesn't require that importers update [09:02:09.0563] > <@jridgewell:matrix.org> Barrels are so convenient, and so awful for performance (both browsers and bundlers) * convenience often makes for better road-paving than good intentions [09:03:04.0365] so importers keep thinking they're doing a fine thing, when they're doing a horrible thing, and the entire ecosystem has to move to `import defer` to avoid the education problem? [09:03:41.0791] education's super hard so maybe that's the right tradeoff to make, tbf [09:10:02.0762] > <@bakkot:matrix.org> but wouldn't this be a recommendation for performance in exactly the same way? it requires work to adopt; it doesn't just give you performance for free It moves the changes from the consumers to the library itself [09:10:08.0224] So 1 instead of N [09:10:47.0173] > <@ljharb:matrix.org> so importers keep thinking they're doing a fine thing, when they're doing a horrible thing, and the entire ecosystem has to move to `import defer` to avoid the education problem? There is currently a choice to make between convenience and performance, and the goal here is to not make them exclusive [09:43:07.0539] does using `import defer` on a barrel file with a bundler that supports it give the ~same results as using deep imports in the first place? [09:43:31.0739] * does using `import defer` on a barrel file with a bundler that supports it give the ~same results as using deep imports in the first place? because treeshaking so far still doesn't fully achieve "only importing what you need in the first place" afaik. [09:46:05.0602] in a language without so many hidden effects all over the place, it would be [09:49:25.0896] > <@ljharb:matrix.org> does using `import defer` on a barrel file with a bundler that supports it give the ~same results as using deep imports in the first place? because treeshaking so far still doesn't fully achieve "only importing what you need in the first place" afaik. Sorry no, it's `export defer`/`export optional` that helps with barrel files [09:49:50.0264] And `export defer` would have the same result as using deep imports [09:50:02.0350] ok, so that's a compelling argument in favor of that one [09:51:34.0893] so `import defer` would be to lessen the downside of importing a barrel file that did not use `export defer`? [09:52:04.0637] And the boundary is still at the `export defer` level, so if you do ```js // main.js import { x } from "library; // library export defer { x, y } from "./x-and-y"; export defer { z } from "./z"; // library/x-and-y export const x = 1; export const y = 2; ``` it will also load `y` (so it's not complete dead code elimination), but gives the building block to remove code if you put unrelated code in separate modules [09:52:09.0264] * And the boundary is still at the `export defer` level, so if you do ```js // main.js import { x } from "library"; // library export defer { x, y } from "./x-and-y"; export defer { z } from "./z"; // library/x-and-y export const x = 1; export const y = 2; ``` it will also load `y` (so it's not complete dead code elimination), but gives the building block to remove code if you put unrelated code in separate modules [09:52:39.0112] * And the boundary is still at the `export defer` level, so if you do ```js // main.js import { x } from "library"; // library export defer { x, y } from "./x-and-y"; export defer { z } from "./z"; // library/x-and-y export const x = 1; export const y = 2; ``` it will also load/run the code for `const y = 2` (so it's not complete dead code elimination), but gives the building block to remove code if you put unrelated code in separate modules [09:52:55.0016] Exactly like deep imports [09:53:14.0206] > <@ljharb:matrix.org> so `import defer` would be to lessen the downside of importing a barrel file that did not use `export defer`? `import defer` is not really related to barrel file [09:53:28.0915] It's in general for "big modules subgraphs" [09:53:40.0275] Even if you have a file with a single export but many dependencies, `import defer` is useful there [09:54:00.0084] One thing that's important to understand is, the barrel file stuff doesn't relate to the motivation for `import defer`--they solve for totally unrelated issues, just both about loading less code [10:14:46.0864] Agree with the various arguments for minimalism here, we just don't have any reason to go into this fractional stuff or BigInts, and IsSafeInteger is a good test. [10:16:38.0985] I agree with Kevin that this isn't especially confusing [10:17:57.0970] * Agree with the various arguments for minimalism here, we just don't have any reason to go into this fractional stuff or BigInts, and IsSafeInteger is a good test. I don't have a strong opinion about any of this and am just happy for range to happen [10:18:06.0226] is there maybe justification to have this produce its own iterator subclass that can have optimised helpers implemented on it? [10:18:33.0888] like `.drop(1e300)` could be implemented very efficiently for this special iterator [10:19:07.0346] I think `drop` and `take` are the only things which could have better implementations and I don't think it's worth doing just for those [10:19:38.0502] and engines could do this optimization anyway [10:20:16.0703] The original proposal includes `BigInt.range()`, don't remember why it is added in first place [10:20:25.0275] I might expect to be able to use `Iterator.range()` to generate unique identifiers, in which case possibly a BigInt type is desirable. [10:21:10.0762] > <@bradfordcsmith:matrix.org> I might expect to be able to use `Iterator.range()` to generate unique identifiers, in which case possibly a BigInt type is desirable. seems a valid use case :P [10:21:11.0214] to waldemar's point, yes, there are some editorial bugs, but nothing that's not fixable [10:21:21.0391] it hasn't passed editorial review yet [10:21:35.0623] > <@bradfordcsmith:matrix.org> I might expect to be able to use `Iterator.range()` to generate unique identifiers, in which case possibly a BigInt type is desirable. oh yeah? [10:21:56.0277] like, give me monotonic ids starting with or something? [10:22:19.0093] i have no bigint range use cases, but "works for one kind of numeric primitive and not the others" seems like a big problem [10:22:55.0359] > <@shuyuguo:matrix.org> like, give me monotonic ids starting with or something? yes, something like that [10:23:28.0592] Are bigints still massively slower than numbers? [10:23:31.0141] I'd prefer it work for bigint just so I'm not having to coerce using `BigInt()` when I need to do math with other bigints. [10:23:31.0609] > <@ljharb:matrix.org> i have no bigint range use cases, but "works for one kind of numeric primitive and not the others" seems like a big problem why is that a big problem? [10:23:36.0139] > <@jridgewell:matrix.org> Are bigints still massively slower than numbers? haha yes [10:23:51.0728] Mathieu Hofman: can you add yourself to the notes? [10:23:57.0368] it's an inconsistency. one of those warts we'd inevitably have to make a "fill in the table" proposal for in a few years. [10:23:59.0128] Timestamps in microseconds as a use case? [10:24:26.0548] it's fine if we want to wait for that, but is there a reason why it's beneficial to defer it? [10:24:28.0683] > <@ljharb:matrix.org> it's an inconsistency. one of those warts we'd inevitably have to make a "fill in the table" proposal for in a few years. not everyone shares that goal though [10:24:53.0581] that is true of most of the goals we all have :-) [10:25:09.0283] right, which is why i pushed back on "seems like a big problem" [10:25:19.0072] it's not that inconsistent - the Math methods do not and never will take Numbers [10:25:23.0971] * it's not that inconsistent - the Math methods do not and never will take BigInts [10:25:35.0052] in any case i was given a concrete use case i found plausible [10:25:38.0744] in fact I don't think we have _anything_ that takes only Number of BigInt? [10:25:39.0682] so i'm happy with bigints being accepted here [10:25:53.0572] but yeah I also prefer accepting BigInt here [10:26:13.0522] > <@eemeli:mozilla.org> Timestamps in microseconds as a use case? I think the idea was to introduce Temporal.Instant ranges for this use case in the future [10:28:10.0892] > <@pchimento:igalia.com> I think the idea was to introduce Temporal.Instant ranges for this use case in the future Maybe `Iterator.range(instant1, instant2, duration)` if `range` is overloaded anyway [10:28:15.0922] > <@pchimento:igalia.com> I think the idea was to introduce Temporal.Instant ranges for this use case in the future * Maybe `Iterator.range(instant1, instant2, duration)` if `range` is overloaded anyway? [10:28:41.0761] > <@bradfordcsmith:matrix.org> I might expect to be able to use `Iterator.range()` to generate unique identifiers, in which case possibly a BigInt type is desirable. can you explain this use case more? I think unique id should be something like crypto.getRandomUUID() [10:29:17.0689] might not need to be unique [10:29:36.0590] also autoincrementing is fine sometimes, not everything has the german tank problem [10:30:00.0346] Python bans floats in range() right? [10:30:46.0389] right [10:32:40.0106] python's popular computing library has `numpy.arange()` which does suffer from unexpected iterations due to floating point errors, and `numpy.linspace()` which does not [10:33:08.0960] ...decimal. [10:33:58.0185] I do like linspace [10:34:13.0086] the options object does leave room for it [10:34:57.0125] You can just use a range of integers and .map for what Matthew is proposing, right? [10:35:02.0238] FWIW it would be nice to have extra time to iron out all the editorial issues as well [10:35:09.0610] then we can have a really solid 2.7 advancement [10:36:23.0494] I'll definitely +1 this feature either with or without fractional values. [10:36:39.0158] same though I do prefer accepting fractional values [10:36:52.0090] but if left out it's not the end of the world [10:41:01.0148] > <@bakkot:matrix.org> the options object does leave room for it I think linspace should be a separate method? [10:41:30.0943] even 2-4x slower must still be faster than `.reduce((a, b) => a + b, -0)` [10:43:58.0486] As the original author of range floating issue, I prefer not accepting franctional values , and change the name from`Iterator.range` to `Iterator.integers` which make it even more clear to developers šŸ˜‰ [10:44:38.0306] `intRange` [10:46:34.0236] Do people also need `ProductExact()` ? [10:46:51.0683] I can't understand the difference in precision/exactitude between precise and exact in this case... can anyone else? [10:47:42.0675] I think "precise" applies to all numbers. So `0.1 + 0.2` has a precise value, it's just not exactly `0.3`. [10:47:45.0962] kinda, i guess there is a notion of "low precision" vs "high precision" that's more commonly used than "low exactitude" vs "high exactitude" [10:47:56.0832] but in this particular case i don't really understand [10:47:59.0640] "Accurate" would be more... accurate. [10:48:21.0363] > <@littledan:matrix.org> I can't understand the difference in precision/exactitude between precise and exact in this case... can anyone else? It just avoid some loss and more close to exact number? [10:48:23.0882] > <@eemeli:mozilla.org> I think "precise" applies to all numbers. So `0.1 + 0.2` has a precise value, it's just not exactly `0.3`. huh? but we're starting with Numbers, not those funny things that we don't have a representation of in JS [10:49:04.0788] > <@shuyuguo:matrix.org> kinda, i guess there is a notion of "low precision" vs "high precision" that's more commonly used than "low exactitude" vs "high exactitude" OK but it's not like we have a precision arg here... it's just supposed to get the right answer [10:49:12.0294] right? [10:49:22.0826] right, in this particular case i don't understand the difference [10:49:41.0299] if it were up to me i'd name it sumSlow [10:50:13.0140] like i would totally believe that if kevin's slides said "this is named sumPrecise, but it may give the wrong impression", someone will then say "sumExact is better" [10:50:15.0934] To be clear, I'm fine with sumExact as a least worst option. [10:50:53.0161] > <@nicolo-ribaudo:matrix.org> Maybe `Iterator.range(instant1, instant2, duration)` if `range` is overloaded anyway? Yes, next follow on might be adding a new Symbol.rangeTo, and then range(a, b, options) where a is an object will do the following: `yield* a[Symbol.rangeTo](b, options)`, then you can add it on Temporal, Decimal (they're not primitive now) and your own class. [10:51:01.0101] > <@nicolo-ribaudo:matrix.org> Maybe `Iterator.range(instant1, instant2, duration)` if `range` is overloaded anyway? * Yes, next follow on proposal might be adding a new Symbol.rangeTo, and then range(a, b, options) where a is an object will do the following: `yield* a[Symbol.rangeTo](b, options)`, then you can add it on Temporal, Decimal (they're not primitive now) and your own class. [10:51:01.0197] Doesn't temporal use `from` ? [10:51:12.0861] what name are we considering? [10:52:16.0817] So, I guess we should go with `Math.sumPreciseFrom()`? [10:52:33.0603] or not... [10:52:37.0067] it sounds like we are not doing `From` based on everyone else doesn't like it [10:52:41.0303] so `sumExact` or `sumPrecise` [10:54:29.0625] > <@shuyuguo:matrix.org> if it were up to me i'd name it sumSlow As I understand, it is not necessarily slow. [10:54:42.0708] dminor: can you clarify your last point in the notes? [10:54:43.0245] yeah it is? [10:54:48.0756] it's necessarily slow_er_ [10:54:50.0160] I was going to say that I dislike `From` because it currently means "I'm creating something of this type from these other values". `Iterator.from` creates an iterator, `Array.from` an array, `Object.fromEntries` an object. If we wanted this to contain from it should be on number (`Number.fromSum`), but also Number is not a collection [10:55:23.0762] Temporal uses `from` in many places and those functions do not take iterables, so I disagree with the Michael Ficarra 's statement that "everything named from takes an iterable" [10:56:03.0724] > <@nicolo-ribaudo:matrix.org> You can just use a range of integers and .map for what Matthew is proposing, right? It's not ergonomic at all: `range(start * stepDivider, end * stepDivider, stepMultipler) | map(x => x / stepDivider)` [10:56:20.0889] > <@nicolo-ribaudo:matrix.org> I was going to say that I dislike `From` because it currently means "I'm creating something of this type from these other values". `Iterator.from` creates an iterator, `Array.from` an array, `Object.fromEntries` an object. If we wanted this to contain from it should be on number (`Number.fromSum`), but also Number is not a collection Agreed. "Foo.from" does imply "takes an iterable", it imples "make a `Foo` from these inputs" [10:56:28.0104] > <@nicolo-ribaudo:matrix.org> I was going to say that I dislike `From` because it currently means "I'm creating something of this type from these other values". `Iterator.from` creates an iterator, `Array.from` an array, `Object.fromEntries` an object. If we wanted this to contain from it should be on number (`Number.fromSum`), but also Number is not a collection * Agreed. "Foo.from" does not imply "takes an iterable", it imples "make a `Foo` from these inputs" [10:57:42.0897] I'm having trouble understanding why we're bothering with an optimization for an error case [10:57:46.0107] I don't think arithmetic commutativity comes into play when we're talking about throwing, which is inherently not arithmetic [10:58:10.0356] @littledan:matrix.org an iterable of Number producing a NaN is not an error case? [10:58:45.0358] I think NaN is kinda usually for error cases? [10:59:05.0953] I get that it's within the domain of Numbers but.... when was the last time you wanted NaN to come up for you? [10:59:08.0733] at the business logic layer, maybe, but at this data processing layer, no [10:59:38.0109] I have to drop, Eemeli and Matthew Gaudet will represent SpiderMonkey for the rest of the day. [11:00:18.0622] > <@michaelficarra:matrix.org> at the business logic layer, maybe, but at this data processing layer, no obviously we need a well-defined answer, but is this going to provide a meaningful speedup meaningfully often? [11:00:38.0045] yes, iterators can yield very many values [11:01:04.0621] a multi-hours' batch could be short-circuited immediately after starting instead of waiting until the end [11:03:16.0007] > <@shuyuguo:matrix.org> it's necessarily slow_er_ As https://en.wikipedia.org/wiki/Pairwise_summation "Pairwise summation is the default summation algorithm in NumPy[8] and the Julia technical-computing language,[9] where in both cases it was found to have comparable speed to naive summation" [11:04:22.0688] eemeli: can you add yourself to the notes doc? [11:09:51.0346] I had to miss the presentation, but why is empty list `-0`? (no opinion, just curious) [11:10:11.0098] it is the identity of floating point addition [11:10:36.0658] @jridgewell:matrix.org `Object.is(-0 + -0, -0)` [11:10:58.0835] (while +0 + -0 = +0) [11:11:20.0612] Why is `-0` the starting point? [11:11:32.0340] If Iā€™m interpreting those correctly.... [11:11:32.0664] what does starting point mean? [11:11:57.0197] `let sum = -0; for (const i of array) sum += i` [11:12:22.0686] so that when `array` is `[-0]` you get the correct answer [11:17:46.0171] The equality question feels very like the question of `==` on value types in Java. [11:19:32.0485] the slides will reference project Valhalla IIRC [11:19:38.0459] value types being the ones with lowercase names? [11:19:43.0209] (i.e. value objects for java) [11:19:59.0459] https://openjdk.org/projects/valhalla/ [11:24:22.0706] this presentation is so well structured šŸ˜ [11:25:58.0649] I really like the composite object approach though I would not call them "CompositeKey" [11:27:42.0113] I linked a few userland implementations here: https://github.com/tc39/proposal-record-tuple/issues/387#issuecomment-2033531920 though I am sure there are others [11:29:02.0219] Composite keys don't seem like a solution to case-insensitive Maps. I still strongly favor equals/hash. [11:29:56.0465] I think they solve a different problem - equals/hash doesn't let me write `groupBy` with the result of my comparator being a composite key (without doing a bunch of work) [11:29:57.0684] but uniqBy without CompositeKey would cover that use case, no? [11:30:11.0763] i.e., `new Map([], { comparer: caseInsensitiveStringComparer }` [11:30:28.0924] @rbuckton:matrix.org it works fine, Unicode has case folding for tht [11:30:51.0739] > <@bakkot:matrix.org> I think they solve a different problem - equals/hash doesn't let me write `groupBy` with the result of my comparator being a composite key (without doing a bunch of work) Wouldn't you just do groupBy over anything you want? [11:31:46.0331] > <@michaelficarra:matrix.org> @rbuckton:matrix.org it works fine, Unicode has case folding for tht It's an extra allocation, likely thrown away, for any given key comparison. equals/hash is defined once and reused [11:32:30.0079] I don't know what that means? I am asking about, for example, I have a list of { name, employer, city } objects, and I want to collect them by `{ employer, city }`. with composite key that's just `Map.groupBy(vals, x => Tuple(x.employer, x.city))`. with equals/hash there's a bunch more ceremony [11:33:48.0586] I have already written a `groupBy` that uses `equals`/hash`, and an equaler that already does tuple structural equality. [11:34:57.0949] That library also supports `[Equatable.equals](obj)` and `[Equatable.hash]()` that can be used if an equaler is not provided, so a given composite key just builds on top of that. [11:35:54.0406] sure, it can certainly be done, there's just a bunch more ceremony [11:36:11.0494] It is far more flexible, IMO. [11:36:35.0816] it's just different [11:36:39.0911] it solves different problems [11:36:50.0888] Having to turn everything into a `CompositeKey` seems like a lot more ceremony to me. [11:37:01.0391] Map.p.getImprecise, anyone? [11:37:06.0158] `CompositeKey` can be built on equals/hash, the other is not true. [11:37:43.0443] `Map.groupBy(vals, x => Tuple(x.employer, x.city))` is very close to zero ceremony [11:37:47.0700] that's exactly how I think about the problem [11:38:02.0084] Plus, AFAIK equals/hash is how every implementation implements maps natively, it's just not exposed to user code. [11:40:25.0551] For that case, maybe. What about `map.set(key, value)` though? You have to write `map.set(new CompositeKey(foo.a, foo.b), value)` or `map.get(new CompositeKey(foo.a, foo.b))`. With equals/hash, you can set it up once and just do `map.set(foo, value)` or `map.get(foo)`. [11:40:54.0638] but if my map is not keyed by `foo`, I don't want it to behave that way? [11:41:01.0683] I want to use a composite key for that case [11:41:02.0171] with a Map constructor hook, you could set it up once too [11:41:16.0022] * with a Map constructor hook, you could set it up once too with composite keys [11:41:19.0943] You can use a composite key for that case, I'd like to *not* have to use composite key for all of the other cases. [11:41:31.0916] > <@rbuckton:matrix.org> It is far more flexible, IMO. it's a lot more risk prone [11:41:35.0103] > <@ljharb:matrix.org> with a Map constructor hook, you could set it up once too with composite keys The same is true for equals/hash. [11:41:58.0424] right. which is why it's not an argument in favor of either one. [11:42:12.0066] > <@mhofman:matrix.org> it's a lot more risk prone It's far more efficient. If I want to write a custom collection that employs a hashtable, I'd rather have it easier to compute a hash. [11:43:02.0144] For the set comparator, it seems like a prototype method `[Symbol.setCompare]` would work, and if Record/Tuple implement that function, then R&T Set/Map semantics should just work [11:43:09.0175] > <@rbuckton:matrix.org> You can use a composite key for that case, I'd like to *not* have to use composite key for all of the other cases. right that's what I mean by "they are different" [11:43:14.0994] JS is a high level language, I don't want low level concept like computing hashes exposed to programmer, especially if they can cause erroneous executions if mishandled [11:43:25.0984] I'm not saying that we should have composite keys _instead of_ hash/equals, just that they solve different problems [11:45:30.0646] (I am _separately_ somewhat skeptical of hash/equals because of the concerns about inconsistency, especially since that seems like it might be a bug farm in engines themselves and not just programs. but that concern is unrelated to this.) [11:50:02.0058] > <@sffc:mozilla.org> For the set comparator, it seems like a prototype method `[Symbol.setCompare]` would work, and if Record/Tuple implement that function, then R&T Set/Map semantics should just work That means the collection correctness is dependent on the stable behavior of its values. I want to use a collection that is resilient against misbehaving values [11:50:28.0218] > <@sffc:mozilla.org> For the set comparator, it seems like a prototype method `[Symbol.setCompare]` would work, and if Record/Tuple implement that function, then R&T Set/Map semantics should just work * That means the collection correctness is dependent on the stable behavior of its values. I want the ability to create a collection that is resilient against misbehaving values [11:55:03.0753] +1 on pretty much everything rbuckton said. [11:58:16.0417] > <@mhofman:matrix.org> That means the collection correctness is dependent on the stable behavior of its values. I want the ability to create a collection that is resilient against misbehaving values If you do not trust the stability of your keys, then you could use a `CompositeKey` and pay the overhead for every get/set. If you do trust the stability of your keys, equals/hash lets you avoid that overhead. [12:00:45.0527] I have a strong preference for fast, efficient JS. `CompositeKey` overhead does not seem fast/efficient. [12:01:19.0339] > <@rbuckton:matrix.org> If you do not trust the stability of your keys, then you could use a `CompositeKey` and pay the overhead for every get/set. If you do trust the stability of your keys, equals/hash lets you avoid that overhead. Not if the only way is for the collection to ask the value. My answer was to sffc who was proposing that [12:01:51.0193] equals/hash does not preclude `CompositeKey`, but rather is the building block a `CompositeKey` could be built on. It's also the building block using a `Uri` as a key could be built on too. [12:02:37.0781] It's not something you could build `CompositeKey` on because `CompositeKey` has `===` equality [12:02:46.0586] and `===` will never invoke a userland `equals` [12:03:22.0843] i missed that there was a configuration of the new direction that has new objects that have special ===? [12:03:28.0265] If we could have `CompositeKey` have `===` equality, why would we not have R&T have `===` equality? [12:03:31.0558] that isn't any more acceptable than the R&T's overloading of === [12:03:51.0610] > <@shuyuguo:matrix.org> i missed that there was a configuration of the new direction that has new objects that have special ===? the CompositeKey thing he presented, with interning, which is already being done in userland [12:03:53.0983] can we change indexOf just for these new objects? [12:04:00.0738] it was not one ACE advocated for [12:04:20.0417] > <@bakkot:matrix.org> the CompositeKey thing he presented, with interning, which is already being done in userland tools compile away === or something? [12:04:34.0001] no I mean they do interning [12:05:03.0311] > <@littledan:matrix.org> can we change indexOf just for these new objects? I assumed 'no', but never asked [12:05:16.0687] > <@bakkot:matrix.org> no I mean they do interning oh by "===" you mean they do interning? [12:05:40.0285] i see, ok [12:05:43.0547] https://github.com/benjamn/immutable-tuple/blob/485b32326349cb0329c749090cebf43f8359fa12/src/tuple.js#L12-L19 [12:06:01.0997] Isn't the discussion around `CompositeKey` predicated on R&T *not* having `===` equality? I have concerns about the GC overhead associated with `CompositeKey` using something like weakmaps/maps as either `CompositeKey("a")` leaks forever, or it requires a FinalizationRegistry and the GC overhead for tracking/collection the object. [12:06:22.0422] > <@rbuckton:matrix.org> Isn't the discussion around `CompositeKey` predicated on R&T *not* having `===` equality? I have concerns about the GC overhead associated with `CompositeKey` using something like weakmaps/maps as either `CompositeKey("a")` leaks forever, or it requires a FinalizationRegistry and the GC overhead for tracking/collection the object. yes, it's all predicated on === not being supported for R&T [12:06:31.0243] rbuckton: that was also my confusion. the resolution is that CompositeKeys have interning semantics built in, they return deduplicated objects such that === just works like object ===, because it's literally deduplicated [12:06:39.0950] or you forbid having a `CompositeKey` that doesn't contain an object, which is what userland does [12:06:50.0347] Interning semantics will have GC overhead [12:06:55.0760] yes indeedy [12:07:35.0555] but it's like, deterministic overhead that's always there, instead of non-deterministic overhead that engines have to tune for and hope they get it right for the largest swath of the code that runs [12:07:35.0943] > <@bakkot:matrix.org> or you forbid having a `CompositeKey` that doesn't contain an object, which is what userland does `CompsiteKey(obj, "a")` works, but `CompositeKey("a", "b")` does not? That seems like a very unstable design. [12:07:45.0699] which is a better place to be in, but... definitely still overhead, yes [12:07:54.0973] I dropped it after presenting the slides to some other people, they felt it complicated the question I was asking [12:08:04.0220] I believe that is NOT was was presented. `#[1] !== #[1] ` [12:08:22.0786] I'm concerned about the overhead of 1000s of `map.get(CompositeKey(a, b))` in a loop. [12:08:34.0530] I kinda like the idea of being able to do something like ``` new Map([], { compare: 'duck' }) ``` and have that "just work". [12:08:34.0675] but when used in collections, the equality used is the one supporting R&T equality [12:08:34.0758] > <@mhofman:matrix.org> I believe that is NOT was was presented. `#[1] !== #[1] ` CompositeKey was presented, it's just not what was proposed [12:10:49.0833] > <@eemeli:mozilla.org> I kinda like the idea of being able to do something like > ``` > new Map([], { compare: 'duck' }) > ``` > and have that "just work". `@esfx/collections-hashmap` and `@esfx/equatable` does this. You can do, say: ```js import { HashMap } from "@esfx/collections-hashmap"; import { Uri } from "./uri.js"; const map = new HashMap(undefined, { equaler: Uri.equaler }); ``` where `Uri.equaler` is just an object with `{ equals(a, b), hash(obj) }`. [12:11:35.0237] I'm confused about the discussion about `CompositeKey` here as it's not what was proposed, but presented as background / historical context. A R/T would, unlike CompositeKey, not have `===` equality. [12:13:14.0203] I was under the impression these were discussed as possible directions to consider, with historical context as to prior discussions within TC39. [12:14:38.0912] rbuckton: I don't want to build R&T on top of a equals / hash mechanism IF there is any way that values can influence the hashing mechanism when added to a collection I created. I am fine if the collection defers to a construction time provided config. [12:14:45.0185] I should have been more clear. I was trying to say `Key(v1, v2) === Key(v1, v2)` is good in that it doesn't introduce a new form of equality. But from what I have heard from engines re R&T and from my own research it seems difficult to make them perform well at scale, the whole app has contention on one global interning table that requires GC hooks to clean up the unused keys. [12:15:01.0056] * rbuckton: I don't want to build R&T on top of a equals / hash mechanism IF there is any way that values can influence the hashing mechanism at the time they're added to a collection I created. I am fine if the collection defers to a construction time provided config. [12:15:16.0519] * rbuckton: I don't want to build R&T on top of a equals / hash mechanism IF there is any way that values can influence the hashing mechanism or any part of the prohgram at the time they're added to a collection I created. I am fine if the collection defers to a construction time provided config. [12:16:08.0346] * rbuckton: I don't want to build R&T on top of a equals / hash mechanism IF there is any way that values can influence the hashing mechanism or any part of the program at the time they're added to a collection I created. I am fine if the collection defers to a construction time provided config. [12:16:23.0327] The upside of equals/hash is that it is a known quantity. Numerous languages and runtimes use this, thus the caveats are known. [12:16:41.0141] A `CompositeKey` based on equals/hash doesn't require complex GC semantics. [12:19:01.0375] If you have a native `CompositeKey` with equals/hash, you just make the "default equaler" behavior used by a `Map` have a specific case for `CompositeKey` vs `===`. But then you can make that "default equaler" an actual object from which to build other equalers from. [12:19:04.0017] To be more clear: `new Map([], {compare: Record.equals})` is fine, and so is `new Map([], {compare: customHashCodeThing}`, but not `new Map([], compareWithHashCode: true)` which would be assuming Records.prototype[Symbol.hashCode]` [12:20:06.0801] * To be more clear: `new Map([], {compare: Record.equals})` is fine, and so is `new Map([], {compare: customHashCodeThing}`, but not `new Map([], compareWithHashCode: true)` which would be assuming looking up a `Records.prototype\[Symbol.hashCode\]\` [12:20:15.0587] * To be more clear: `new Map([], {compare: Record.equals})` is fine, and so is `new Map([], {compare: customHashCodeThing}`, but not `new Map([], compareWithHashCode: true)` which would be assuming looking up a `Records.prototype[Symbol.hashCode]` [12:21:46.0991] In other plenary sessions it has come up that the language should be as close to fully deterministic as we can. e.g. not exposing a browser-sniffing API. `Object.hash` feels like it would need to be fully deterministic across engines to meet these goals. Perhaps I have mis-read committee comments with this goal. [12:22:56.0661] > <@mhofman:matrix.org> To be more clear: > `new Map([], {compare: Record.equals})` is fine, and so is `new Map([], {compare: customHashCodeThing}`, but not `new Map([], compareWithHashCode: true)` which would be assuming looking up a `Records.prototype[Symbol.hashCode]` I've experimented with something like `[Symbol.hashCode]`. In well written applications its perfectly fine and would serve most users well. If you don't trust your inputs, you could opt-out with a custom equaler that doesn't call an `@@hashCode`. That said, I'm more interested in a general purpose `{ equals, hash }` mechanism for `Map`/`Set` and various other methods that do equality checks, than i am with instance-overrideable hash codes. [12:23:12.0520] I also wonder if even string hashing is not as easy to standardize in 262 due to the spec being in wtf-16 and runtimes potentially using different encodings? [12:32:50.0075] We don't automatically need to expose the result of the internal hashing. We could have special values for `compare` [12:33:50.0673] > <@aclaymore:matrix.org> In other plenary sessions it has come up that the language should be as close to fully deterministic as we can. e.g. not exposing a browser-sniffing API. `Object.hash` feels like it would need to be fully deterministic across engines to meet these goals. > Perhaps I have mis-read committee comments with this goal. `Object.hash()` *shouldn't* be fully deterministic, at least not for strings. String hash code generators benefit from randomness as it evens out clumping due to collisions over the course of various app restarts. An implementation would also want to be able to swap from one hash algorithm to another from version to version as hash algorithms improve. In .NET, you can control whether to use a randomized string hashing algorithm via configuration: https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/userandomizedstringhashalgorithm-element. [12:34:20.0207] > <@mhofman:matrix.org> We don't automatically need to expose the result of the internal hashing. We could have special values for `compare` I considered describing it as an opaque value, but you need to be able to do math on it. [12:35:16.0223] To follow up on Ashley Claymore 's comment, I would be opposed to any new APIs to expose non determinism to the program. It's fine for these things to remain internal to the engine, but I don't want them to be observable [12:37:46.0581] Or should I say, the new API would have to be extremely well motivated, like FR/WeakRef, but I don't think hashcode would clear that line [12:38:12.0317] For example ```js class Point { #x; #y; constructor(x, y) { this.#x = x; this.#y = y; } get x() { return this.#x; } get y() { return this.#y; } static equaler = { equals(a, b) { return #x in a && #x in b && a.#x == b.#x && a.#y == b.#y; }, hash(obj) { let hc = Object.hash(obj.#x); hc = ((hc << 7) | (hc >>> 25)) ^ Object.hash(obj.#y); return hc; } } } ``` [12:38:16.0030] * Or should I say, the new API would have to be extremely well motivated and fairly self contained, like FR/WeakRef, but I don't think hashcode would clear that line [12:38:31.0572] * For example ```js class Point { #x; #y; constructor(x, y) { this.#x = x; this.#y = y; } get x() { return this.#x; } get y() { return this.#y; } static equaler = { equals(a, b) { return #x in a && #x in b && a.#x === b.#x && a.#y === b.#y; }, hash(obj) { let hc = Object.hash(obj.#x); hc = ((hc << 7) | (hc >>> 25)) ^ Object.hash(obj.#y); return hc; } } } ``` [12:40:02.0241] I honestly don't understand why `hashLike(obj) { return #[obj.#x, obj.#y] }` is not an acceptable approach`. Engines are definitely capable of optimizing this [12:40:24.0427] * I honestly don't understand why `hashLike(obj) { return #[obj.#x, obj.#y]; }` is not an acceptable approach. Engines are definitely capable of optimizing this [12:40:55.0573] * I honestly don't understand why `hashLike(obj) { return #[Point, obj.#x, obj.#y]; }` is not an acceptable approach. Engines are definitely capable of optimizing this [12:41:52.0424] Capable does not mean willing or likely to do so. [12:43:20.0808] And that still doesn't let me write a hashtable. I need something I can use as a numeric index with good avalanche properties. Maybe 95% of users don't need this, but its a headache for the small percent that do for good reason. [12:43:33.0007] * And that still doesn't let me write a hashtable. I need something I can use as a numeric index with good avalanche properties. Maybe 95% of users don't need this, but its a headache for the small percent that do need it for a good reason. [12:48:02.0971] For example, I had to do quite a bit to workaround this limitation when experimenting with the shared structs dev trial. I had a very strong need for a concurrent Map, which required implementing a hash table. It would have been far easier if I could actually generate a hash for a string or an object: https://github.com/microsoft/TypeScript/blob/shared-struct-test/src/compiler/sharing/collections/concurrentMap.ts Yes, maybe we would want to add a `ConcurrentMap` after we add shared structs, but that's certainly not going to be in the MVP. [12:55:51.0531] generating a non-deterministic value from a basic value like and object or a string feels like a non starter for me. I'd claim it's not needed by 99.9999% of JS authors [12:56:24.0171] Hash bucket lookup is approximately O(1), while a user-built collection relying on manually comparing tuples is O(n), and if you can't compare tuples using `===`, it's O(n*m) where `m` is the number of keys in the tuple. [13:01:49.0975] > <@mhofman:matrix.org> generating a non-deterministic value from a basic value like and object or a string feels like a non starter for me. I'd claim it's not needed by 99.9999% of JS authors Then call the method `Object.nondeterministicHash(obj)` or `Object.randomHash(obj)` or document the non-deterministic nature on MDN. If you were an implementer that built internally used murmur3 for string hashing and found you could speed up all applications by a significant percentage by switching to xxhash64, wouldn't you do so? `Object.hash()` must be explicitly "non-deterministic" from the get-go just so anyone consuming it could reap the same benefits if a better hash algorithm comes along. [13:02:05.0810] > <@mhofman:matrix.org> generating a non-deterministic value from a basic value like and object or a string feels like a non starter for me. I'd claim it's not needed by 99.9999% of JS authors * Then call the method `Object.nondeterministicHash(obj)` or `Object.randomHash(obj)` or document the non-deterministic nature on MDN. If you were an implementer that internally used murmur3 for string hashing and found you could speed up all applications by a significant percentage by switching to xxhash64, wouldn't you do so? `Object.hash()` must be explicitly "non-deterministic" from the get-go just so anyone consuming it could reap the same benefits if a better hash algorithm comes along. [13:03:20.0984] Case in point, even if you're using the non-random string hash algorithm in .NET, `Object.GetHashCode()` is still considered to be non-deterministic as upgrading the version can change the hash algorithm to one that is more efficient. [13:03:27.0652] I just don't think there is sufficient motivation to expose this in the language [13:09:22.0821] I'd argue that the number of times we've discussed mechanisms for customizing equality indicates it *is* sufficient motivation. The main reason I want custom equality for Map/Set at all is related to performance. You can use them as-is as long as you're willing to sacrifice performance and memory at the cost of the overhead introduced by alternatives. I want custom `Map` equality so that I *don't* have to `.toString()` every Uri I use as a key in a `map.get()`. Forcing custom collections to operate at O(n) while native collections can have O(1) because they can cheat and do the hashtable lookup is not the answer when your algorithms are performance-critical. [13:18:40.0627] I'm not saying that `Object.hash()` must return a number, but that you could conceivably do all the things you would need to do to implement a hash table for an alternative to be a reasonable compromise. For example, let's say we had an API like this: ```ts interface Equaler { equals(a: T, b: T): boolean; hash(a: T): opaque; } ``` where `hash` returns an opaque value. I'd need, at a minimum, something like this as well: ```ts declare const defaultEqualer: Equaler; declare function combineHash(a: opaque, b: opaque): opaque; interface HashArray { length: number; [hash: opaque]: T; ... } interface SharedHashArray { length: number; [hash: opaque]: T; ... } ``` to have any possibility of achieving similar perf to it being a number. [13:18:47.0429] You can customize equality using the `hashLike` approach I suggested above. It is also optimizable by engines. [13:19:46.0383] The combine hash is exactly what creating a R/T with your components does [13:20:13.0774] But not for a custom collection. As I said, native `Map`/`Set` could cheat and unwrap a composite key to do a hashtable lookup for O(1), but a custom collection would be O(n) at best. [13:21:27.0155] I think a design that ignores custom collections is too short sighted. [13:22:09.0308] Sorry I don't understand how your opaque hash is any different than a R/T [13:22:39.0343] The opaque hash is just a number you can't actually see in to. [13:22:55.0324] * The opaque hash is just a number you can't actually see into. [13:23:21.0962] The only reason it would be opaque is so that users can't depend on the actual value. [13:23:40.0428] If it were a tuple, you couldn't use it as an index into an array of hash buckets. [13:24:03.0324] If it were a number, or an opaque value that's actually just a number, then you conceivably could. [13:24:33.0110] Numbers are just far easier to reason over since we already have `Array` and shared structs would have `SharedArray`. [13:25:42.0475] To use a hash code efficiently, I need to be able to compare it using `===`, shift it using `<<` and `>>>`, and combine it using `^`. All of these operations are extremely fast using 32-bit integers. [13:25:59.0563] * To use a hash code efficiently, I need to be able to compare it using `===`, shift it using `<<` and `>>>`, combine it using `^`, and use it as the index into an array. All of these operations are extremely fast using 32-bit integers. [13:28:16.0323] `#[a, b, c]` has no avalanche properties I can use to ensure proper distribution in a hashtable, and cannot be used as an index, so comparing keys using `#[a, b, c]` in a custom collection is at least O(n*3), since I must compare the elements of every key in the map to the elements in the provided key. It's terribly inefficient. [13:32:43.0378] > <@rbuckton:matrix.org> If it were a tuple, you couldn't use it as an index into an array of hash buckets. You could use it as a key in a Map, how is it different? [13:33:00.0991] not across threads [13:33:29.0336] if the "opaque" number value is actually observable by the program, even indirectly, it defeats the purpose of being opaque [13:34:29.0428] And that assumes that all JS devs will only ever need `Map` and `Set`, and that there are no other collection classes JS does not implement that will ever be needed. [13:34:47.0291] when you said opaque, I had assumed actually opaque, like a unique symbol [13:35:05.0532] anything else is not opaque by my definition [13:35:39.0396] I don't need it to be opaque. I'm saying the only way an opaque value would work for me is if it could have the same properties I would need from an integer hash code. [13:35:41.0782] * when you said opaque, I had assumed actually opaque, like a unique symbol, or empty object [13:36:38.0780] The problem is that if it's not a 32-bit integer, it becomes a boxed value in most implementations, and those are going to be slower. [13:49:09.0555] Let me summarize my thoughts: - Any mechanism to customize equality for `Map` and `Set` should be done in a way that could be leveraged by a custom collection. Anything less is too short sighted. - Using a composite key/tuple will not give me the performance characteristics I would need in a custom collection, as it would result in O(n*m) lookup time. - Equals/hash can be employed by a hashtable in a custom collection for O(1) lookup time. - Using a composite key/tuple would not be compatible with custom collections under shared memory multithreading, which are likely to be necessary since concurrent/synchronized collections are not part of the shared structs MVP. - Composite keys require an allocation that is likely to be repeated for every call to `map.get`. Engines *could* optimize, but likely won't do so immediately, if ever. - A composite key mechanism can be implemented on top of equals/hash such that a WeakMap/Map-based registry is unnecessary. equals/hash cannot be implemented on top of a composite key. [13:51:11.0114] * Let me summarize my thoughts: - Any mechanism to customize equality for `Map` and `Set` should be done in a way that could be leveraged by a custom collection. Anything less is too short sighted. - Using a composite key/tuple will not give me the performance characteristics I would need in a custom collection, as it would result in O(n\*m) lookup time. - Equals/hash can be employed by a hashtable in a custom collection for O(1) lookup time. - Using a composite key/tuple would not be compatible with custom collections under shared memory multithreading, which are likely to be necessary since concurrent/synchronized collections are not part of the shared structs MVP. - Composite keys require an allocation that is likely to be repeated for every call to `map.get`. Engines _could_ optimize, but likely won't do so immediately, if ever. - A composite key mechanism can be implemented on top of equals/hash such that a WeakMap/Map-based registry is unnecessary. equals/hash cannot be implemented on top of a composite key. - Equals/hash can use 32-bit integers, `<<`, `>>>` and `^`, which are all already fast and optimized. Opaque values require boxing, making them slow (see bigint performance as an example) [13:52:37.0428] I'm saying that any value that is a source of observable non determinism does not seem sufficiently motivated for a "keying" mechanism when there are alternatives that cover most of the use cases except performance, which can be optimized, or shared memory, which is a hypothetical future, and could have its own collections API. JS is a high level language that does not expose low level / internal details of its objects or memory layout. exposing hash code would change that [13:54:48.0532] > Composite keys require an allocation that is likely to be repeated for every call to map.get. Engines could optimize, but likely won't do so immediately, if ever. The idea with R&T is that, being objects and arrays. In many cases they are the data structure, so there is no extra allocation for these cases [13:54:57.0037] * > Composite keys require an allocation that is likely to be repeated for every call to map.get. Engines could optimize, but likely won't do so immediately, if ever. The idea with R&T is that, being objects and arrays. In many cases they are the data structure, so there is no extra allocation for these cases [13:56:17.0493] As I've discovered while discussing array iterator performance, "can be optimized" isn't a useful metric. Implementations won't optimize unless they have very good reason to. Array destructuring has been around for ~9 years and still isn't optimized. [13:59:13.0053] > <@aclaymore:matrix.org> > Composite keys require an allocation that is likely to be repeated for every call to map.get. Engines could optimize, but likely won't do so immediately, if ever. > > The idea with R&T is that, being objects and arrays. In many cases they are the data structure, so there is no extra allocation for these cases @bakkot's example in matrix earlier shows this to not be true. The keys are often a subset of the data structure, and will very likely be generated on the fly. [13:59:17.0855] If the data model of the application is already using R&T. Inserting these into a map could be the fastest case. Because all the hashing and equality operations are entirely native, with zero entry into userland minimizing guards and allowing JIT. [13:59:52.0082] > The keys are often a subset of the data structure If this really is common, then apps could keep those parts of the data structure seperate [13:59:56.0813] * > The keys are often a subset of the data structure If this really is common, then apps could keep those parts of the data structure seperatee [14:00:01.0528] * > The keys are often a subset of the data structure If this really is common, then apps could keep those parts of the data structure seperate [14:00:10.0433] > <@aclaymore:matrix.org> If the data model of the application is already using R&T. Inserting these into a map could be the fastest case. Because all the hashing and equality operations are entirely native, with zero entry into userland minimizing guards and allowing JIT. You're only proving my point. `Map` and `Set` can only have O(1) performance for a tuple as a composite key if they use equals/hash internally. Custom collections cannot do the same. [14:00:36.0340] Custom collections can use Map/Set internally [14:00:57.0804] * Custom collections can use Map/Set internally for the vast majority of custom use cases [14:00:58.0993] Yes. I said earlier. The implementations must use hash + equals at some layer. I 100% agree that the pattern is efficent [14:01:01.0993] custom collections aren't an ephemeral thing, there is production code that uses them. The perf hit is likely the only reason they aren't used more. [14:01:38.0881] The custom collections I've needed were wrappers around Maps [14:01:56.0511] I'd be interested in hearing about other cases. I'm familiar with the ConcurrentMap one. [14:07:58.0577] > <@aclaymore:matrix.org> The custom collections I've needed were wrappers around Maps This is often the case, but these are often still inefficient. You often have to do some type of conversion for every `get`/`set`, which can result in unnecessary allocations. You could build a custom map that does case insensitive string comparisons on keys by passing them through a case folding algorithm, but that's terribly inefficient. For a case insensitive comparison, you might use case folding on the string to compare, but then you're also producing another string in memory. If you want to preserve the input key as written, you have to store both the original key and value as the map entry, resulting in another allocation and duplication, plus the need to wrap `keys()`, `values()`, etc. This is all unnecessary overhead. [14:09:47.0851] Gotcha. That matches my understanding. It's not that lots of custom collections can't be built, but they will need to do more allocations. [14:11:48.0467] > <@aclaymore:matrix.org> Gotcha. That matches my understanding. It's not that lots of custom collections can't be built, but they will need to do more allocations. Map/Set wrappers can be built with allocation overhead, but only if their keys are easily translated to something that works as an existing key. Complex objects and up requiring O(n) [14:12:51.0128] Even if you are using weakmap/map registries to ensure a composite key has the same identity, that's *still* O(n) if you're writing `map.get(CompositeKey(a, b))`. You're just paying the O(n) cost walking the registry. [14:13:34.0450] i.e., for n steps, you're doing an O(1) operation for each step. [14:13:44.0773] what is n here? [14:13:52.0855] n is the number of elements in the composite key. [14:14:12.0420] ok. I thought you were referring to collection size and got confused [14:14:17.0041] https://docs.google.com/presentation/d/1JfChmW8tQ2_mrFDynosNqa1tjJ2j-qX6WoKm8vc_tkY/edit#slide=id.g2c6eebea946_0_40 [14:14:38.0673] Sorry, I should have said m instead of n for that case. [14:15:56.0824] > <@aclaymore:matrix.org> Gotcha. That matches my understanding. It's not that lots of custom collections can't be built, but they will need to do more allocations. * Map/Set wrappers can be built with allocation overhead, but only if their keys are easily translated to something that works as an existing key. Complex objects end up requiring O(n) [14:17:37.0348] > <@rbuckton:matrix.org> Let me summarize my thoughts: > > - Any mechanism to customize equality for `Map` and `Set` should be done in a way that could be leveraged by a custom collection. Anything less is too short sighted. > - Using a composite key/tuple will not give me the performance characteristics I would need in a custom collection, as it would result in O(n\*m) lookup time. > - Equals/hash can be employed by a hashtable in a custom collection for O(1) lookup time. > - Using a composite key/tuple would not be compatible with custom collections under shared memory multithreading, which are likely to be necessary since concurrent/synchronized collections are not part of the shared structs MVP. > - Composite keys require an allocation that is likely to be repeated for every call to `map.get`. Engines _could_ optimize, but likely won't do so immediately, if ever. > - A composite key mechanism can be implemented on top of equals/hash such that a WeakMap/Map-based registry is unnecessary. equals/hash cannot be implemented on top of a composite key. > - Equals/hash can use 32-bit integers, `<<`, `>>>` and `^`, which are all already fast and optimized. Opaque values require boxing, making them slow (see bigint performance as an example) we actually added Map and Set as built-ins without having a way that what they do could be done in a custom collection. Of course it would be nice to support efficient maps defined in JS if possible given all of the other goals and constraints, but I don't see why it makes sense to say that no advances should be made for Map and Set without managing to support this other goal. [14:18:40.0207] the composite-ness of composite keys or R&T doesn't actually depend on any new built-in thing, but identity hashcodes continue to not be exposed [14:20:59.0554] > <@littledan:matrix.org> we actually added Map and Set as built-ins without having a way that what they do could be done in a custom collection. Of course it would be nice to support efficient maps defined in JS if possible given all of the other goals and constraints, but I don't see why it makes sense to say that no advances should be made for Map and Set without managing to support this other goal. We did, and this has continued to come up regularly since. I'm not opposed to things that make `Map` and `Set` more efficient. I'm concerned with choosing an API design that caters to native maps and sets that could never be efficiently implemented in a custom collection. We either end up never improving the story for custom collections, or end up with two independent mechanisms for customizing equality. [14:22:49.0391] A composite key that uses a weakmap registry just to ensure reference identity for equivalent keys is entirely unnecessary with hash/equals. If we were to then implement hash/equals to improve the custom collection story, then composite keys become an evolutionary dead end for the language. [14:23:40.0314] Is anyone defending CompositeKey? [14:24:00.0251] Object-based R&T seems like a cleaner mechanism and doesn't do anything which should make things harder for custom collections, I think [14:24:25.0664] I'm not sure what distinction between CompositeKey and object-based R&T you're drawing [14:24:29.0103] How is object-based R&T not a composite key? [14:25:05.0210] The distinction I would make would be between a WeakMap-registry based composite key, or a Map/Set privileged CompositeKey. [14:27:55.0096] A Map/Set-privileged CompositeKey has a way forward to equals/hash, but then why not have equals/hash instead (or at the same time). [14:29:48.0437] > <@bakkot:matrix.org> I'm not sure what distinction between CompositeKey and object-based R&T you're drawing well, the basic version of R&T that we were discussing is, there isn't support for being keys in WeakMap, so the whole registry thing just goes away [14:30:01.0835] and there's no support for === [14:30:18.0493] I guess the registry was more for ===; WeakMap doesn't actually need it [14:31:09.0816] so I guess you're calling this Map/Set privileged? But I don't see what Map/Set are doing that other collections couldn't also do [14:32:18.0237] > <@rbuckton:matrix.org> A Map/Set-privileged CompositeKey has a way forward to equals/hash, but then why not have equals/hash instead (or at the same time). This is something I have trouble understanding. Even if we do want to generalize to hash, the question of "why not instead" was answered--you want a nice default mechanism for the normal case, as other languages tend to have [14:32:18.0888] I'd also like more details from the folks that are concerned about non-determinism as to why that should matter. It would be guaranteed to be deterministic so long as the app/browser is executing. Assuming no string-random-hash algorithm at the outset, the only thing that would change the hash result would probably be upgrading the browser as a newer runtime could choose a more efficient algorithm. [14:32:46.0987] my preferred approach is to allow composite objects (i.e. interned objects, that give you `===`) as keys in WeakMaps iff they contain at least one thing which could itself be in a WeakMap, and to introduce a `canBeWeaklyHeld` predicate so that's easier to determine. [14:32:54.0591] why not at the same time was also answered: because there is not consensus to expose hashcodes, due to interop risk [14:33:32.0907] so I guess you want more details... what do you mean by details? [14:33:49.0191] > <@littledan:matrix.org> so I guess you're calling this Map/Set privileged? But I don't see what Map/Set are doing that other collections couldn't also do No, Map/Set privileged would mean a Map would treat a CompositeKey as a special value and essentially do equals/hash natively. Without runtime support for equals/hash, a custom collection cannot do the same. [14:34:01.0340] > if you're writing `map.get(CompositeKey(a, b))`. You're just paying the O(n) cost walking the registry. I do not understand. How is it `O(n)` if there is a Map that supports a native composite keying ? [14:34:36.0176] > <@littledan:matrix.org> This is something I have trouble understanding. Even if we do want to generalize to hash, the question of "why not instead" was answered--you want a nice default mechanism for the normal case, as other languages tend to have Other languages tend to have equal/hash, with a few built-in equalers you can readily reference. I would certainly want those as well. [14:34:41.0505] > <@rbuckton:matrix.org> No, Map/Set privileged would mean a Map would treat a CompositeKey as a special value and essentially do equals/hash natively. Without runtime support for equals/hash, a custom collection cannot do the same. hmm, what kind of custom collection are you imagining? I guess I'm not picturing it properly [14:35:12.0407] > <@mhofman:matrix.org> > if you're writing `map.get(CompositeKey(a, b))`. You're just paying the O(n) cost walking the registry. > > I do not understand. How is it `O(n)` if there is a Map that supports a native composite keying ? That was in reference to the WeakMap registry mechanism on https://docs.google.com/presentation/d/1JfChmW8tQ2_mrFDynosNqa1tjJ2j-qX6WoKm8vc_tkY/edit#slide=id.g2c6eebea946_0_40 [14:40:28.0160] > <@rbuckton:matrix.org> I'd also like more details from the folks that are concerned about non-determinism as to why that should matter. It would be guaranteed to be deterministic so long as the app/browser is executing. Assuming no string-random-hash algorithm at the outset, the only thing that would change the hash result would probably be upgrading the browser as a newer runtime could choose a more efficient algorithm. I want a language where I can deterministically reproduce execution. If you exclude I/O (which encompasses Date.now and Math.random), we currently have that language [14:41:29.0085] > <@littledan:matrix.org> hmm, what kind of custom collection are you imagining? I guess I'm not picturing it properly https://github.com/esfx/esfx/blob/main/packages/collections-hashmap/src/index.ts, though its essentially just a `Map` that takes an `{ equaler }` option, as I'm suggesting. https://github.com/microsoft/TypeScript/blob/shared-struct-test/src/compiler/sharing/collections/concurrentMap.ts, which is a lock-free concurrent map that cannot use `Map` as a backing store. https://github.com/microsoft/TypeScript/blob/shared-struct-test/src/compiler/sharing/collections/sharedMap.ts, which is a coarse-grained synchronized map. to name a few. In other languages you're likely to see custom collections where specific semantics for get/set are necessary, such as with observable collections in .NET. [14:44:23.0678] > <@mhofman:matrix.org> I want a language where I can deterministically reproduce execution. If you exclude I/O (which encompasses Date.now and Math.random), we currently have that language But for what purpose is this important? Is it for testing? If you're going to exclude `Date.now` and `Math.random`, why would you not exclude `Object.hash`? Could an implementation not have a `--hash-algorithm=...` flag to ensure a stable hash algorithm for tests? [14:45:25.0011] If it's for security, wouldn't a randomized Object.hash be preferred over a deterministic one? [14:46:22.0153] Are there any "implementation defined" behaviors you also have to discount? `Object.hash` would essentially be implementation defined. [14:48:16.0078] > <@rbuckton:matrix.org> But for what purpose is this important? Is it for testing? If you're going to exclude `Date.now` and `Math.random`, why would you not exclude `Object.hash`? Could an implementation not have a `--hash-algorithm=...` flag to ensure a stable hash algorithm for tests? We actually use this determinism for replay of program execution, sometimes across engines. Yes we could remove, but then it increases the difference with what programs may expect being in the language. [14:48:37.0197] There's also the possibility we could define hashing as `Object.hash(obj, algorithm?)` where you can optionally specify `"stable"` or `"fast"`. [14:49:15.0376] * There's also the possibility we could define hashing as `Object.hash(obj, algorithm?)` where you can optionally specify `"stable"` or `"fast"` (or similar). [14:50:10.0893] > <@mhofman:matrix.org> We actually use this determinism for replay of program execution, sometimes across engines. Yes we could remove, but then it increases the difference with what programs may expect being in the language. And how does this differ from the unique reference identity that a CompositeKey might have at runtime? [14:52:04.0943] > <@rbuckton:matrix.org> And how does this differ from the unique reference identity that a CompositeKey might have at runtime? all usages of such unique identity would be deterministic. [14:52:06.0796] It should be clearly called out in documentation that `Object.hash()` is not stable across application restarts and shouldn't be serialized. I would expect that code that respects that would work just fine when replaying execution. [14:52:19.0197] It isn't deterministic. [14:52:28.0293] how isn't it? [14:52:47.0712] * how isn't it? observably ? [14:52:53.0638] It's unobservably non-deterministic. An object reference at runtime can exist in a different memory address each time you execute. [14:53:23.0911] sure but I don't care about internals of the engine that are not observable [14:53:33.0517] If your code treats a hash as unobservably non-deterministic, there is no difference. [14:53:54.0575] it's like saying a JS object representation may live at a different pointer, that is just not relevant [14:53:57.0959] The problem is that you can't make a hash code opaque without severely impacting the performance. [14:54:27.0287] And the whole point is the performance. [14:54:53.0789] I care about what the language exposes, that it not allow JS program to have non deterministic behaviors. I do not control the programs that run in my environment [14:55:46.0870] JS allows programs to have non-deterministic behaviors, hence Date.now(), Math.random(). You can only make that assertion about a subset of the language. [14:57:20.0452] Would control over the algorithm assuage that concern? Be it that from a CLI option or an option to a ShadowRealm, or an optional argument to `Object.hash()`? [14:57:38.0121] right, and any further non-deterministic behaviors would IMO have to be extremely well motivated, and I am not seeing that from hashCode [15:13:34.0522] I would find a custom equality solution that cannot be implemented efficiently in a pure-JS collection to be a serious design flaw in the language. If Map/Set have privileged support for a composite key, that will end up poisoning other APIs over time. It will inevitably be reused by `groupBy` and `distinct`, or possibly a future `join`/`crossJoin`/`leftJoin` for iterators. We will move further and further away from the ability for the runtime to evolve in userland as no userland implementation could have the same performance characteristics. And we _regularly_ suggest that some proposals instead evolve in userland. [15:14:55.0068] I'd like to point out one specific bit. `Object.hash()` is essentially deterministic for everything *except* strings. The only non-determinism in strings would be if a more efficient hashing algorithm were to come along. [15:18:16.0247] It's possible that we could handle that case with a `subtle`-like API. Get consensus on a base algorithm for string hashing, and allow users to use a faster algorithm as it comes along. For example, were `Object.hash` to use `mumur3` by default, we could later support xxhash via `Object.hash.xxhash32` or `Object.hash(v, "xxhash32")`. [15:18:24.0671] * It's possible that we could handle that case with a `subtle`-like API. Get consensus on a base algorithm for string hashing, and allow users to use a faster algorithm as it comes along. For example, were `Object.hash` to use `mumur3` by default, we could later support xxhash via `Object.hash.xxhash32()` or `Object.hash(v, "xxhash32")`. [15:20:43.0522] Then the program remains deterministic. If randomized string hashing becomes important, make that an option but add that narrow bit to the set of things you don't support if you want determinism instead of throwing out the whole feature. [15:27:45.0576] Correction, strings are not the only case. `Object.hash({})` might also need to be non-deterministic to avoid introducing a side-channel. I have to think on that a bit more. [15:28:50.0891] It could just be that `Object.hash({})` always returns zero or throws, and you have to work around that yourself, but that would be unfortunate. [15:40:06.0913] IMO, I'd rather have a non-deterministic `Object.hash()` and better support for time-travel debugging across engines to handle the "replayable program execution" case. [16:12:08.0740] Mathieu Hofman: It's my understanding that iteration over Map() and Set() is always in the order in which items were added. This wouldn't be affected by allowing a userland-provided hashCode form of equals, would it? Are you thinking of some other way in which non-determinism would creep in? Are you concerned userland bugs causing the hashCode for a mutable object to change when the object is mutated? [16:16:54.0172] > <@bradfordcsmith:matrix.org> Mathieu Hofman: It's my understanding that iteration over Map() and Set() is always in the order in which items were added. This wouldn't be affected by allowing a userland-provided hashCode form of equals, would it? Are you thinking of some other way in which non-determinism would creep in? Are you concerned userland bugs causing the hashCode for a mutable object to change when the object is mutated? I'm not saying that the builtin collections would use hashCode for order. I'm saying exposing more non-determinism would intrinsically risk causing more non-deterministic execution of JS programs. [16:28:48.0280] Ok, so the issue isn't that the proposed use of `Symbol.hashCode` and `Symbol.equals` properties (or something like that) for Map/Set equality is itself a cause of non-determinism. It is rather that any userland implementation of `Symbol.hashCode` is likely to be non-deterministic in order to be functional. And, other uses of `Symbol.hashCode` that may crop-up in user-land could then lead to non-deterministic behavior. The existence of this API practically requires that userland code produce non-deterministic values in order to use it. Is that right? [16:50:02.0493] My understanding is that for hashCode to be effective, some non-determinism will become observable. That non-determinism comes from APIs that would need to be introduced to hash "root" values, like strings or the identity of some values (e.g. unique symbols) 2024-04-10 [17:01:03.0754] If you care about performance, it's a bad idea to feed external input into a deterministic hash function. That invites attacks such as Hash Flooding: https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding [17:40:26.0514] > <@waldemarh:matrix.org> If you care about performance, it's a bad idea to feed external input into a deterministic hash function. That invites attacks such as Hash Flooding: https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding V8 rotates its hash function on boot, but at some point there was a security issue where, if you put some Maps in the startup snapshot (which Chrome doesn't do, but Node.js started doing at some point), it forgot to re-rotate them, causing a DOS risk of this form! Fixed by joyee :) [18:49:54.0096] I am not sure what we're supposed to get from Project Valhalla when primitives/value semantics has already been deemed not an option due to implementability issues [18:51:07.0105] if the question is class-based vs object-based immutable things, I think there's a lot of interest among JS developers in having easy-to-use mechanisms that aren't based on classes. [18:51:21.0072] * if the question is class-based vs object-based immutable things, I think there's a lot of interest among JS developers in having easy-to-use mechanisms for immutable data structures that don't force them to write classes [18:54:02.0864] > <@waldemarh:matrix.org> If you care about performance, it's a bad idea to feed external input into a deterministic hash function. That invites attacks such as Hash Flooding: https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding Performance and/or security. This is one of the reasons why I indicated non-determinism for string hashing between application restarts is a good thing, actually. [18:54:30.0944] There's a lot of complexity with class-based immutable things, e.g., we'd need to use initializer list-based constructors, so if there's subclassing it's a totally different instantiation protocol. And if they're value types, it's even more complicated if we want to avoid any kind of global registries like we discussed with shared structs. [18:55:48.0841] > <@rbuckton:matrix.org> Performance and/or security. This is one of the reasons why I indicated non-determinism for string hashing between application restarts is a good thing, actually. Right so given that it's simultaneously a good thing and a bad thing, we've so far opted to hide the hashcodes [19:00:51.0335] The "bad thing" case (non-determinism being bad) seems so narrowly focused that I have a hard time believing it should apply broadly across the language. [19:04:06.0637] I don't know that class-based vs object-based is a meaningful distinction in JS, but in any case, the main things I want to take away are - even in a language with a well-established idiom for .equals/.hash, having the ability to conveniently create objects which are `==` each other is useful - they've explored some of the relevant space already, such as having WeakMaps which reject these values (though, this being Java, that behavior is customizable) [19:16:51.0726] The overhead incurred by a `CompositeKey` just seems like a non-starter to me. Maybe we could find another approach that can overcome these caveats. For example, a `Hasher` class instead of a global `Object.hash()`. A given `Hasher` could just use a monotonically increasing number for each unique object instance it comes across, and maybe is configurable as to how it handles string hashing (e.g., algorithm, randomness, seed values, etc.). Then your equaler looks like `{ equals(a, b), hash(obj, hasher) }` and you then can write `new Map([], { equaler, hasher })`. Object hashes could be deterministic without being a global communications channel. String hashing can be deterministic if you want it to be, or not, as best fits your scenario. [19:33:30.0028] yes, I agree that having objects which === each other is useful; it's unfortunate that we can't go in that direction per implementer feedback, but I think we can still find many related but different improvements to JavaScript. [19:34:53.0019] * yes, I agree that having values which === each other based on their contents is useful; it's unfortunate that we can't go in that direction per implementer feedback, but I think we can still find many related but different improvements to JavaScript. [19:35:20.0038] Class-based vs object-based is more about syntax and conventions. I think these sorts of features are more likely to have broad adoption with convenient syntax, especially with something related to object/array syntax. If we had classes, we'd have to think through the instantiation protocol. But it's also useful to look at other issues in this area which don't relate to those. [19:35:37.0526] * Class-based vs object-based is more about syntax and conventions. I think these sorts of features are more likely to have broad adoption with convenient syntax, especially with something related to object/array syntax. If we had class syntax, we'd have to think through the instantiation protocol. But it's also useful to look at other issues in this area which don't relate to those. [19:35:50.0373] * Class-based vs object-based is more about syntax and conventions. I think these sorts of features are more likely to have broad adoption with convenient syntax, especially with something related to object/array syntax. If we had class syntax, we'd have to think through how `new` works. But it's also useful to look at other issues in this area which don't relate to those. [19:36:46.0538] > The overhead incurred by a CompositeKey just seems like a non-starter to me. If this means one which is interned so that === works, I agree [19:37:21.0938] > <@rbuckton:matrix.org> The "bad thing" case (non-determinism being bad) seems so narrowly focused that I have a hard time believing it should apply broadly across the language. How do you think we should work through the situation where a bunch of people directly disagree with this judgement? [19:49:13.0090] That's why I mentioned alternatives in the preceding message. I'm willing to consider alternatives that support the scenarios I've discussed. I'd entertain an opaque hash if it were feasible to actually support those scenarios in a performant way. A Composite key cannot satisfy those scenarios as it can never be fast in a custom collection, only in native Map/Set. Maybe I'd be less concerned if a `ConcurrentMap` were in the MVP for shared structs, but I know that shared structs *without* concurrent collections is already a hard sell. The problem is that shared structs *without* concurrent collections is nearly unusable for my use cases without the ability to implement a fast efficient custom collection. To support it in the dev trial I essentially had to tag every shared struct with a monotonically increasing identity to use as the hash, and implement a string hashing algorithm just so I could roll my own `ConcurrentMap`. That approach has a lot of overhead I'd rather avoid. [19:53:42.0680] tl;dr, I don't need hash/equals if I know I'll get `ConcurrentMap`, but I know that's a long shot at this point. I'd still lament the overhead of a `CompositeKey`, but my primary use cases would be covered. On the other hand, hash/equals means I'm less concerned about when, if ever, I get `ConcurrentMap` since I could readily implement it in userland. [20:04:00.0639] it was not clear to me that this feedback applied to interning at construction time, with a constructor function rather than syntax [20:05:05.0279] and being actual `typeof "object"` objects rather than a new kind of primitive, and so on [20:05:15.0176] https://matrixlogs.bakkot.com/TC39_Delegates/2024-04-09#L294-L290 [23:06:40.0145] Could a hashcode be a global symbol for which `Symbol.keyFor()` returned `undefined`? Wouldn't that avoid the concerns about non-determinism? [01:43:01.0918] Symbols don't help non-determinism. ``` typeof hash("a"); // "symbol" if (hash("a") === hash("b")) { print("foo"); } ``` [01:43:02.0462] Does the program print foo? [01:47:59.0303] If the spec says that the symbol hash of every value is different and never equal, then we haven't hashed into a smaller space. If the spec says which values have the same hash then this opens up code to collisions attacks. If the spec says it's random which values have the same hash then it's non-deterministic if the program prints foo. is my understanding of the problem statement with the various design constraints put forward from committee. [02:14:42.0430] > <@rbuckton:matrix.org> Plus, AFAIK equals/hash is how every implementation implements maps natively, it's just not exposed to user code. Ashley Claymore: I understood from this earlier assertion by Ron that implementations have already found ways to square the circle with respect to "different and never equal", but maybe I misunderstood? [02:18:32.0400] Hashes can have conflicts ā€” in a Map you store a list of entries per each hash. When looking up values in the map, you: 1. Compute the has of the key 2. Get the list corresponding to that hash 3. Iterate through the list to check if the key is indeed there [02:26:45.0807] > <@eemeli:mozilla.org> Ashley Claymore: I understood from this earlier assertion by Ron that implementations have already found ways to square the circle with respect to "different and never equal", but maybe I misunderstood? Yep. The important part is that this isn't exposed. Apart from trying to measure the execution time. Doing `map.set("a", 1)` I can't tell if there was a hash collision and it had to probe, or the buckets needed to be-resized [02:53:08.0319] Ah, got it. So for a "different and never equal" sort of hash, `hash()` would need to remember all the values that have passed through it. A related thought I had that could limit the impact of that would be making the hash (or composite key, not really sure how they'd be very different) linked to the lifetime of an object. Then `hash(foo, "a")` and `hash(bar, "a")` would never equal if `foo !== bar`, but `hash(obj, { a: 1, b: 2}) === hash(obj, { b: 2, a: 1 })` could work. [05:05:43.0408] That is essentially the CompositeKey / R&T design. That doesn't help Ron's use case of building high performance custom hash maps. To implement custom hash maps the only truely useful hashing function is one where it is possible for values to collide, otherwise the space hasn't been reduced to something that can fit into a small number of indexable buckets [05:07:00.0418] this is why Ron desires a hashing function that returns integers [05:16:11.0364] I would like to think that the object based R&T provide lots of value without needing to expose hashing. While also not precluding another proposal for exposing hashing function for the use cases when full control and minimal object allocations are desirable. For use cases when R&T can be adopted as the data model of the application, this would be efficient. Using these values as Map/Set keys would require not extra allocation (outside of the map/set's own storage naturally), and the hashing and equality functions would be 100% native with zero need for userland re-entrancy guards. However Ron is correct that when R&T need to be created as keys because the data model and the keys are not directly compatible then those application will need to allocate more objects to create the keys. This should be more memory efficient than the current solution of flattening values into one long string, but is still an extra allocation that a purely manual hashing+equality interface would avoid. [05:26:00.0271] > <@rbuckton:matrix.org> The overhead incurred by a `CompositeKey` just seems like a non-starter to me. Maybe we could find another approach that can overcome these caveats. For example, a `Hasher` class instead of a global `Object.hash()`. A given `Hasher` could just use a monotonically increasing number for each unique object instance it comes across, and maybe is configurable as to how it handles string hashing (e.g., algorithm, randomness, seed values, etc.). Then your equaler looks like `{ equals(a, b), hash(obj, hasher) }` and you then can write `new Map([], { equaler, hasher })`. Object hashes could be deterministic without being a global communications channel. String hashing can be deterministic if you want it to be, or not, as best fits your scenario. In this hasher scenario, I don't really understand why not just use the object as a representative of its own binary hash, and I don't understand what you're proposing for compound values [05:28:05.0258] > <@bakkot:matrix.org> it was not clear to me that this feedback applied to interning at construction time, with a constructor function rather than syntax We didn't present the interning idea at that pivotal TC39 meeting because it had already been ruled out for the reasons Ashley presented about the cost of interning, based on previous conversations with implementers (which we maybe should've repeated in committee) [05:29:07.0569] shu: Could you clarify whether "better" is good enough, when it comes to interning overhead? [05:30:50.0686] I want to compare object equality structurally, not by reference. The most efficient way to do so for custom equality in a hash table is to calculate a hashcode for bucketing, and use an equals method against each element in the bucket. A user-defined hash table cannot use object reference identity as it is not a numeric value, instead it needs an identity hash. [05:32:34.0045] Can you convert an object identity into a number by having a `WeakMap`, and assigning a number to each object? [05:36:29.0865] let's see if we can avoid these weak things where possible... it has a real GC perf cost [05:37:20.0790] rbuckton: If your goal is to have a concurrent map, maybe we should focus on that. That could work both by identity (by default) and converting to a R&T for structural comparison (opt in with a keyBy function). Would that implement what you need? [05:37:57.0079] That is what is generally done to work around this currently, but it has drawbacks: - It's not resilient to version conflicts when multiple versions of the same package are installed. - If globally accessible, it must be randomized to avoid becoming a communications channel. - It doesn't work across threads for something like shared structs. It's also only part of the problem with hashing, the other problem is strings. [05:47:18.0707] a concurrent map would hash strings well, right? [05:47:34.0390] > <@rbuckton:matrix.org> That is what is generally done to work around this currently, but it has drawbacks: > - It's not resilient to version conflicts when multiple versions of the same package are installed. > - If globally accessible, it must be randomized to avoid becoming a communications channel. > - It doesn't work across threads for something like shared structs. > > It's also only part of the problem with hashing, the other problem is strings. How can you solve these problems with a custom data structure? [05:48:00.0664] I'm not sure what you mean by "this is what is done" -- the concurrent map construct doesn't exist yet [05:49:52.0354] > <@littledan:matrix.org> rbuckton: If your goal is to have a concurrent map, maybe we should focus on that. That could work both by identity (by default) and converting to a R&T for structural comparison (opt in with a keyBy function). Would that implement what you need? One of my goals is performance. Composite keys, or using R&T as keys, is going to have overhead. If I want to use a `Uri` or a `Point` or a `Location` as a key, I would have to convert it to a R&T type or composite key first, which is an allocation for *every* call to `get`/`set`/`has`. This can be 1000s of allocations in a tight loop, and if I don't own `Uri` or `Point` or `Location` I can't just convert those to be R&T types. An `{ equals, hash }` object is a single allocation that is reused for every key. Another of my goals is maturing the language. Custom collection classes can't perform as well as native `Map`/`Set` because developers don't have access to the requisite core capabilities necessary to make that happen. I'm concerned that composite keys either become evolutionary dead end for the language if these building blocks become available, or they become a rationale to never make these building blocks available and thus we never have the flexibility to write efficient custom collections. [05:51:50.0436] > <@littledan:matrix.org> I'm not sure what you mean by "this is what is done" -- the concurrent map construct doesn't exist yet I was responding to nicolo. Also, a concurrent map *does* exist. I had to build one for TypeScript as part of experimenting with shared structs. The lack of an `Object.hash()` required a significant number of workarounds that I wouldn't want to rely on in a released product. [05:54:07.0166] Yes, I can see how composite keys or R&T has more runtime cost than a hashcode. That is sometimes the compromise that we make in high-level languages. I guess when domain-specific hacks are possible, they can be included as the keyBy. [05:54:15.0547] > <@rbuckton:matrix.org> I was responding to nicolo. Also, a concurrent map *does* exist. I had to build one for TypeScript as part of experimenting with shared structs. The lack of an `Object.hash()` required a significant number of workarounds that I wouldn't want to rely on in a released product. ah, now I understand why you identify strings as an issue [05:54:17.0014] And yes, if concurrent collections were part of the MVP for shared structs then I might be less concerned, but I don't see that being likely. [05:54:30.0178] Strings are an issue for multiple reasons. [05:55:37.0206] > <@rbuckton:matrix.org> And yes, if concurrent collections were part of the MVP for shared structs then I might be less concerned, but I don't see that being likely. I can understand your concern there but in this chat we've identified some serious concerns with exposing identity hashcodes... maybe it would work better to push on ConcurrentMap as a follow-on for shared structs, rather than these lower-level utilities in other parts of the language. [05:55:59.0859] - Implementations need to be able to choose more efficient string hashing algorithms as they become available, and thus requires non-determinism between upgrades. - Strings are a notorious source for hash collisions, and thus requires non-determinism between app restarts. [05:56:49.0047] > <@rbuckton:matrix.org> - Implementations need to be able to choose more efficient string hashing algorithms as they become available, and thus requires non-determinism between upgrades. > - Strings are a notorious source for hash collisions, and thus requires non-determinism between app restarts. these sound like issues if you're going to implement your own string hashtable; if this task is offloaded to the VM, it might be easier [06:01:02.0347] > <@littledan:matrix.org> I can understand your concern there but in this chat we've identified some serious concerns with exposing identity hashcodes... maybe it would work better to push on ConcurrentMap as a follow-on for shared structs, rather than these lower-level utilities in other parts of the language. I need more context as to why this is a serious concern. The argument's I've heard for only advancing deterministic APIs do not seem convincing to me. Non-determinism is good for security. Non-determinism is good for performance. Non-determinism is necessary for a large percentage of existing applications. The only rationale I've heard is replay of execution, but there are other technologies for that and it depends on denial of `Math.random()` and `Date.now()` (and `Temporal.Now`, and numerous other sources of randomness). `Object.hash()` could just as easily be denied or made deterministic to serve that case. Determinism for the sake of Determinism does not serve the web platform. [06:02:27.0279] We already know that we must not expose a deterministic identity hashcode operation. But we also have serious issues around nondeterminism. I'm especially concerned with the interop risks over time. This all is why we're currently taking the middle path of hiding the identity hashcode. [06:04:10.0611] userland re-entracy is also a common concern for proposals. If the equals function is written in userland, this puts userland re-entracy right at the heart of the map internal bucket probing loop. I'll leave it to engine implementations to state how much of a concern that is to them. [06:04:35.0056] what do you mean by reentrancy here? [06:04:43.0421] leaving C++ [06:04:51.0244] back to the application's logic [06:05:00.0328] which could re-enter the currently executing function [06:05:09.0408] > <@littledan:matrix.org> We already know that we must not expose a deterministic identity hashcode operation. But we also have serious issues around nondeterminism. I'm especially concerned with the interop risks over time. This all is why we're currently taking the middle path of hiding the identity hashcode. Can you expand on your concerns about interop risks? A hashcode is not a portable value. It is only relevant during the life of the application. That is the case in every language as far as I know. [06:06:01.0585] * which could re-enter the currently executing function. Invalidating pointers, if this re-entrancy was not taken into account. e.g. the buckets being re-sized while walked [06:06:42.0416] It's easy to imagine someone depending on string hashcodes having certain properties [06:07:21.0077] for example you could check whether the hashcode has changed as an indicator of whether your JS program has restarted, which feels off [06:09:35.0562] > <@littledan:matrix.org> I can understand your concern there but in this chat we've identified some serious concerns with exposing identity hashcodes... maybe it would work better to push on ConcurrentMap as a follow-on for shared structs, rather than these lower-level utilities in other parts of the language. Getting shared structs through committee is going to be an uphill battle. Concurrent collections are a necessity to make them practically useful in large-scale applications. If the MVP for shared structs suddenly became stage 4 today I still wouldn't be able to use them for TypeScript without having to build my own concurrent `Deque`, Work stealing queue, `ConcurrentMap`, and `ConcurrentBag`/`ConcurrentSet`. [06:10:22.0241] > <@rbuckton:matrix.org> Getting shared structs through committee is going to be an uphill battle. Concurrent collections are a necessity to make them practically useful in large-scale applications. If the MVP for shared structs suddenly became stage 4 today I still wouldn't be able to use them for TypeScript without having to build my own concurrent `Deque`, Work stealing queue, `ConcurrentMap`, and `ConcurrentBag`/`ConcurrentSet`. sounds like a good argument that we should have a built-in ConcurrentMap, whether as part of shared structs or a follow-on proposal [06:10:40.0117] > <@littledan:matrix.org> for example you could check whether the hashcode has changed as an indicator of whether your JS program has restarted, which feels off There are easier ways to do this that don't depend on `Object.hash()` [06:10:51.0733] shu: I hear you have concerns re: import defer and it's usability for the web. happy to talk about it [06:11:09.0970] "this is an uphill battle" doesn't *necessarily* imply that taking the battle somewhere else will make it easier [06:11:42.0664] > <@littledan:matrix.org> "this is an uphill battle" doesn't *necessarily* imply that taking the battle somewhere else will make it easier There's nowhere else to take it, IMO. [06:12:32.0535] > <@littledan:matrix.org> It's easy to imagine someone depending on string hashcodes having certain properties Pretty much every hash generation API in every language include documentation that hash codes are not stable across application restarts. [06:23:28.0899] > <@littledan:matrix.org> Yes, I can see how composite keys or R&T has more runtime cost than a hashcode. That is sometimes the compromise that we make in high-level languages. I guess when domain-specific hacks are possible, they can be included as the keyBy. JS performance is a hot button issue in the ecosystem. The "why not rewrite in Rust" crowd is growing louder and louder, especially now that WASM-GC is available, and the easier it becomes for people to move off of JS. I want the JS language to evolve and mature, to continue to be relevant. I don't want to end up with JS just being for hobbyists because the rest of the world has moved on. We mature by adding flexibility, smoothing rough edges, improving performance, improving ergonomics, simplifying common tasks, and adding new capabilities that open the doors for new classes of applications. JS is not a specialized language, it is a general purpose language, and that's its strength. [06:23:54.0785] > <@littledan:matrix.org> Yes, I can see how composite keys or R&T has more runtime cost than a hashcode. That is sometimes the compromise that we make in high-level languages. I guess when domain-specific hacks are possible, they can be included as the keyBy. * JS performance is a hot button issue in the ecosystem. The "why not rewrite in Rust" crowd is growing louder and louder, especially now that WASM-GC is available, and its becoming easier and easier for people to move off of JS. I want the JS language to evolve and mature, to continue to be relevant. I don't want to end up with JS just being for hobbyists because the rest of the world has moved on. We mature by adding flexibility, smoothing rough edges, improving performance, improving ergonomics, simplifying common tasks, and adding new capabilities that open the doors for new classes of applications. JS is not a specialized language, it is a general purpose language, and that's its strength. [06:24:04.0744] * JS performance is a hot button issue in the ecosystem. The "why not rewrite in Rust" crowd is growing louder and louder, especially now that WASM-GC is available, and it's becoming easier and easier for people to move off of JS. I want the JS language to evolve and mature, to continue to be relevant. I don't want to end up with JS just being for hobbyists because the rest of the world has moved on. We mature by adding flexibility, smoothing rough edges, improving performance, improving ergonomics, simplifying common tasks, and adding new capabilities that open the doors for new classes of applications. JS is not a specialized language, it is a general purpose language, and that's its strength. [06:39:56.0817] * Pretty much every hash generation API in every language includes documentation that hash codes are not stable across application restarts. [06:50:44.0512] good morning/evening/night delegates! meeting starting in ~10m. [06:58:29.0228] > <@yulia:mozilla.org> shu: I hear you have concerns re: import defer and it's usability for the web. happy to talk about it will take you up on that [07:03:56.0101] > <@littledan:matrix.org> shu: Could you clarify whether "better" is good enough, when it comes to interning overhead? not at this time [07:04:22.0971] I would volunteer for note taking, but I'm getting pinged on non-TC39 things a lot right now. [07:05:42.0480] > <@aardvark179:matrix.org> I would volunteer for note taking, but I'm getting pinged on non-TC39 things a lot right now. understandable, thanks anyway! [07:23:46.0164] hard agree with USA here [07:24:18.0690] > <@michaelficarra:matrix.org> hard agree with USA here could you elaborate? [07:26:57.0813] stage 1 acknowledges that localisation is important for the web and something we should try to address within TC39; that remains the case, regardless of how long it will take or how hard it might be to provide a solution [07:30:09.0310] > <@michaelficarra:matrix.org> stage 1 acknowledges that localisation is important for the web and something we should try to address within TC39; that remains the case, regardless of how long it will take or how hard it might be to provide a solution Where is the point where you're disagreeing with USA? [07:30:27.0427] ... nowhere? [07:30:43.0151] oh oops I misread sorry [07:31:02.0423] somehow turned into hard disagree [07:39:24.0546] > <@rbuckton:matrix.org> JS performance is a hot button issue in the ecosystem. The "why not rewrite in Rust" crowd is growing louder and louder, especially now that WASM-GC is available, and it's becoming easier and easier for people to move off of JS. I want the JS language to evolve and mature, to continue to be relevant. I don't want to end up with JS just being for hobbyists because the rest of the world has moved on. We mature by adding flexibility, smoothing rough edges, improving performance, improving ergonomics, simplifying common tasks, and adding new capabilities that open the doors for new classes of applications. JS is not a specialized language, it is a general purpose language, and that's its strength. How would a concurrent map actually be implemented in the context of wasm-gc? Isn't there the same kind of problems that you have an opaque identity for your objects like in JS? I actually still fail to understand what the data model is for your use case, and as such what the solution space might look like. You want structural comparisons for your collection entries, but these values are shared? How can a structural comparison be stable if the data itself can change from under you? Could part of the "solution" be to make R/T sharable and valid values inside shared structs? IMO, the ability to shim new features is highly preferable, but it rarely needs to be efficient / performant, nor does it need to be ergonomic for the shim implementer. I'd rather have a cohesive language that doesn't expose sharp edges. [07:40:01.0774] > <@rbuckton:matrix.org> JS performance is a hot button issue in the ecosystem. The "why not rewrite in Rust" crowd is growing louder and louder, especially now that WASM-GC is available, and it's becoming easier and easier for people to move off of JS. I want the JS language to evolve and mature, to continue to be relevant. I don't want to end up with JS just being for hobbyists because the rest of the world has moved on. We mature by adding flexibility, smoothing rough edges, improving performance, improving ergonomics, simplifying common tasks, and adding new capabilities that open the doors for new classes of applications. JS is not a specialized language, it is a general purpose language, and that's its strength. * How would a concurrent map actually be implemented in the context of wasm-gc? Isn't there the same kind of problems that you have an opaque identity for your objects (and possibly strings) like in JS? I actually still fail to understand what the data model is for your use case, and as such what the solution space might look like. You want structural comparisons for your collection entries, but these values are shared? How can a structural comparison be stable if the data itself can change from under you? Could part of the "solution" be to make R/T sharable and valid values inside shared structs? IMO, the ability to shim new features is highly preferable, but it rarely needs to be efficient / performant, nor does it need to be ergonomic for the shim implementer. I'd rather have a cohesive language that doesn't expose sharp edges. [07:40:51.0753] +1 to finding ways to reduce champion burnout. There's a reason why it was 2022 when I was presented R&T :) [07:41:31.0887] it has not reached an impasse [07:44:39.0128] * +1 to finding ways to reduce champion burnout. There's a reason why it was 2022 when I last presented R&T :) [07:50:33.0472] I was scrolling through Twitter and found another case of somebody not realizing that `using` requires a binding even if it's not actually needed: https://twitter.com/nullvoxpopuli/status/1777364717805142411 [07:51:19.0038] Maybe we could allow `using LeftHandSideExpression` [07:51:22.0947] * Maybe we could allow `using LeftHandSideExpression ;` [07:51:48.0623] It would beed a cover grammar, but `using fn()` is much better than `using void = fn()` [07:52:22.0545] I used "impasse", or "stuck" in the issue, to indicate that no actions only within TC39 are sufficient for further progress. The impasse may be resolved by actions outside the committee, i.e. industry feedback and/or adoption. But didn't want to get into an argument about semantics. [07:52:42.0873] > <@nicolo-ribaudo:matrix.org> It would beed a cover grammar, but `using fn()` is much better than `using void = fn()` And then `using void =` would be a syntax error like `let void =` and `const void =` [07:53:31.0683] @nicolo-ribaudo:matrix.org https://github.com/tc39/proposal-discard-binding/issues/1#issuecomment-2030365690 [07:55:12.0177] Thank you [07:59:11.0671] I really like allow redeclare `_` ... [08:05:45.0046] https://johnnyreilly.com/typescript-eslint-no-unused-vars [08:06:07.0877] ^^ for reference to what RBN said about eslint config [08:07:08.0825] I think the Risks and Assumptions section in the Java JEP for unnamed variables is interesting: > We assume that little if any actively-maintained code uses underscore as a variable name. Developers migrating from Java 7 to Java 22 without having seen the warnings issued in Java 8 or the errors issued since Java 9 could be surprised. They face the risk of dealing with compile-time errors when reading or writing variables named _ and when declaring any other kind of element (class, field, etc.) with the name _. [08:07:25.0551] This just feels risky to me, maybe it's better to keep this in extractors and pattern matching and explicit resource management, even if it would be nice to have it elsewhere. [08:07:48.0608] fwiw eslint supports an ignore prefix _pattern_ for the reason ron just mentioned [08:08:46.0837] > <@dminor:mozilla.org> This just feels risky to me, maybe it's better to keep this in extractors and pattern matching and explicit resource management, even if it would be nice to have it elsewhere. it seems kinda weird to me to restrict this construct to just pattern matching and explicit resource management, but if we can't have it elsewhere, that's maybe OK [08:09:31.0970] I agree it would be weird [08:09:57.0043] But there's no risk of breaking existing code, and we can have `_` there, right? [08:10:56.0584] i don't understand how any existing variable name can be web compat [08:11:09.0332] It would be nice to use `_` in pattern matching, given precedent in other languages with pattern matching [08:11:33.0333] `const _ = a, _ -= b` is illegal everywhere [08:11:39.0956] > <@shuyuguo:matrix.org> i don't understand how any existing variable name can be web compat this is if it's restricted to the new constructs [08:11:40.0009] > <@shuyuguo:matrix.org> i don't understand how any existing variable name can be web compat Instead of "redeclaring a variable is an error in strict mode" it becomes "reading a variable that has been redeclared in strict mode is an error" [08:11:42.0633] * `const _ = a, _ = b` is illegal everywhere [08:12:09.0540] > <@nicolo-ribaudo:matrix.org> Instead of "redeclaring a variable is an error in strict mode" it becomes "reading a variable that has been redeclared in strict mode is an error" "strict mode" -> I mean let, const, and strict params [08:12:10.0613] but `var _ = 1, _ = 2;` is not [08:12:15.0471] * but `var _ = 1, _ = 2;` is not illegal anywhere [08:12:29.0884] > <@ljharb:matrix.org> but `var _ = 1, _ = 2;` is not illegal anywhere And nobody is proposing changing how it works [08:12:53.0212] I don't like `_` as discard. What's wrong with `void`? Is there any pushback on `void`? [08:13:01.0002] what about `function f(_, _) {}`, which is legal in sloppy mode? [08:13:06.0430] > <@nicolo-ribaudo:matrix.org> Instead of "redeclaring a variable is an error in strict mode" it becomes "reading a variable that has been redeclared in strict mode is an error" sloppy mode so popular though? [08:13:13.0598] To me "void" sounds an awful lot like "undefined". [08:13:45.0745] for C/C++ developers it could be a source of confusion, yes [08:13:47.0296] and indeed the binding is not defined :-p [08:14:30.0672] > <@shuyuguo:matrix.org> sloppy mode so popular though? The error cases remain the same, with the difference that the error is moved from declaration position to reference position [08:14:45.0685] So what works in strict mode would keep working the same [08:14:49.0267] I assume other languages could cope with `_` easier because they didn't have a popular library that was commonly imported as `_` [08:14:56.0025] * So what works in any mode would keep working the same [08:15:16.0881] > <@nicolo-ribaudo:matrix.org> The error cases remain the same, with the difference that the error is moved from declaration position to reference position i am still confused what the proposed semantics is for sloppy mode [08:15:38.0075] @rbuckton:matrix.org Why do we lose assignment patterns with `_`? [08:15:39.0408] > <@aclaymore:matrix.org> I assume other languages could cope with `_` easier because they didn't have a popular library that was commonly imported as `_` it's on us for not doing modules/namespaces well I guess [08:15:43.0798] > <@aclaymore:matrix.org> I assume other languages could cope with `_` easier because they didn't have a popular library that was commonly imported as `_` * it's on us for not doing modules/namespaces well at start I guess [08:15:52.0525] The simple case is just to allow redeclaring `_`, no other restrictions. [08:15:52.0603] > <@shuyuguo:matrix.org> i am still confused what the proposed semantics is for sloppy mode vars and function params can be redeclared, but if you redeclare a let/const then it throws when you reference the binding [08:16:02.0946] > <@usharma:igalia.com> it's on us for not doing modules/namespaces well at start I guess How would that change things? People do `import _ from "lodash"` [08:16:10.0366] I am totally fine restricting this feature to strict mode [08:16:33.0391] > <@aclaymore:matrix.org> How would that change things? > People do `import _ from "lodash"` Renaming a static import is trivial. [08:16:46.0538] Sure, but popular? [08:17:01.0235] plus the `_` name is less necessary with imports [08:17:20.0959] but if all you can do is pollute the global namespace, `$` and `_` abound. [08:17:22.0122] They should be using `import { foo, bar } from ā€˜lodashā€™` anywaysā€¦ [08:17:49.0377] they should be using `import foo from 'lodash.foo'` anyways, to avoid the CVE noise :-p [08:18:00.0080] nothing wrong with namespace imports [08:18:03.0865] > <@nicolo-ribaudo:matrix.org> vars and function params can be redeclared, but if you redeclare a let/const then it throws when you reference the binding okay well, i don't think this proposal meets the bar thus far for complicated binding handling [08:18:45.0160] > <@bakkot:matrix.org> nothing wrong with namespace imports in this case i'm complaining about big-bag-o-things packages, not namespace imports [08:18:54.0548] > <@bakkot:matrix.org> nothing wrong with namespace imports Too many people mistake it for a mutable object, or use it dynamically in a way the minifier canā€™t eliminate _everything_ else. [08:19:28.0214] well don't do that [08:19:30.0797] ^ the latter is also a problem with barrel files [08:19:41.0744] but put the blame where the problem is, not on something else [08:20:03.0985] the-world-if-people-didnt-do-things-they-shouldnt-do.jpg [08:21:38.0595] > <@aclaymore:matrix.org> I assume other languages could cope with `_` easier because they didn't have a popular library that was commonly imported as `_` you should see the bananas stuff I've heard about in Python [08:22:11.0737] yes please! I have done very little Python and am all ears! [08:22:32.0803] https://docs.djangoproject.com/en/5.0/topics/i18n/translation/ [08:23:09.0061] Okay, it's not really bananas - but they're doing the same thing [08:23:15.0924] > <@shuyuguo:matrix.org> okay well, i don't think this proposal meets the bar thus far for complicated binding handling We donā€™t need the ā€œreference the bindingā€ restriction. [08:23:39.0483] that would be more palatable [08:23:49.0545] > <@aclaymore:matrix.org> I assume other languages could cope with `_` easier because they didn't have a popular library that was commonly imported as `_` * you should see the ~bananas stuff~ similar stuff I've seen in Python [08:24:45.0983] aliasing `gettext()` to `_()` is a fairly common pattern for programming languages that have an implementation of gettext [08:25:00.0895] I'm really interested in extractors effectively providing runtime types: https://github.com/tc39/proposal-extractors/issues/20 [08:25:08.0060] because the `xgettext` tool extracts strings inside `_()` out of source files into message files [08:25:18.0203] Justin Ridgewell: you mean bindings to `_` work in a last-write-wins manner and so there's no ambiguity? [08:25:32.0136] Yes [08:25:32.0198] * Justin Ridgewell: you mean bindings to `_` work in a last-write-wins manner and so there's no ambiguity error? [08:25:55.0540] Itā€™s just a regular binding, but it can be redeclared. [08:26:23.0883] I like rust's thing where a redeclaration introduces a new scope which shadows the previous one [08:26:33.0004] so closures which capture the old one still work [08:26:39.0962] at least I assume that's how it works, I haven't actually checked [08:26:47.0673] now that I'm saying this I have no idea why I believe it [08:26:58.0989] With extractors, you can have ``` function foo(String(s), Number(n)) { ... } ``` and be sure of `s` being a string and `n` being a number. [08:27:14.0323] I think that's right [08:27:49.0124] i think you'd need `String(const s)` etc to make the binding tho? [08:27:54.0776] Nope. [08:27:58.0864] > <@bakkot:matrix.org> so closures which capture the old one still work The closure would have to move the value in. Iā€™m not sure it works the way you think. [08:29:03.0501] @eemeli:mozilla.org what `Symbol.customMatcher` implementation are you thinking of for the built-in constructors? [08:30:06.0606] those are in the pattern matching proposal, fwiw [08:30:12.0143] * those are already in the pattern matching proposal, fwiw [08:30:28.0214] * those are already in the pattern matching proposal, fwiw, with brand checking semantics [08:30:51.0041] ... brand checking or typeof? [08:30:56.0316] Something like ``` class Number { ... [Symbo.customMatcher](subject) { if (typeof subject === 'number') return [subject]; if (subject instanceof Number) return [Number(subject)]; return false; } } ``` [08:31:21.0465] * Something like ``` class Number { ... [Symbol.customMatcher](subject) { if (typeof subject === 'number') return [subject]; if (subject instanceof Number) return [Number(subject)]; return false; } } ``` [08:31:33.0154] I am categorically opposed to accepting boxed primitives here or anywhere [08:31:38.0386] it'd check for the internal slot of a Number instead of instanceof, but otherwise yes [08:31:52.0609] > <@jridgewell:matrix.org> The closure would have to move the value in. Iā€™m not sure it works the way you think. Actually you canā€™t reference `_` in a closure (Iā€™ve never tried before): https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ab28412522dba608f6964c9a11ce600f [08:32:18.0124] > <@bakkot:matrix.org> I am categorically opposed to accepting boxed primitives here or anywhere it doesn't make any sense to reject boxed primitives if people are going to be referencing the constructor [08:32:27.0053] i don't expect it to come up in practice either way tho, tbf [08:32:38.0354] > <@bakkot:matrix.org> I am categorically opposed to accepting boxed primitives here or anywhere * it doesn't make any sense to reject boxed primitives if people are going to be referencing the _constructor_ [08:32:41.0875] sure it does, `Number(foo)` doesn't produce a boxed primitive [08:32:44.0217] I don't care about boxed primitives tbh. [08:32:55.0952] it's a constructor. `new Number(foo)` does [08:33:05.0594] if the extractor used `new` I would agree with you [08:33:08.0081] but it doesn't [08:33:15.0844] `const new Number(n) = 5` [08:33:23.0096] so you want the requirement to be `new UserClass()` to match a user class? [08:33:35.0333] * so you want the requirement to be `new UserClass` as a pattern to match a user class? [08:33:42.0655] `new Number` is not a valid ExtractorMemberExpression. [08:34:10.0610] @eemeli:mozilla.org sorry I forgot the Kappa [08:34:13.0495] iow, a pattern of `Number` and a pattern of `UserClass` should work the same - either both require new or neither does [08:34:16.0329] I'll go back to TDZ [08:34:25.0593] and i don't see any argument for requiring `new` on non-primitive constructors [08:34:50.0469] `UserClass(foo)` should throw or either behave the same as `new UserClass(foo)`, so it's fine for `let UserClass(foo) = bar` to be the extractor pattern [08:35:03.0442] but `Number` doesn't work like that, so we can't use that as precedent for how `Number` should work [08:35:21.0689] not if it's an ES5 class [08:35:40.0625] and many predicates are constructible (because they're not written as arrows or concise methods) [08:35:54.0916] * and many predicates are technically constructible (because they're not written as arrows or concise methods) [08:36:01.0426] * and many predicates are technically constructible (because they're not written as arrows or concise methods etc) [08:36:11.0827] I don't think "this is technically constructible" ought to guide our design of these features [08:36:25.0786] this applies to user-defined predicates and also to `Number` [08:36:29.0518] ok. but we don't actually have a clear thing in JS that divides constructors and functions, so PascalCase is the universal convention that JS devs use for that [08:37:14.0488] so, designing for that universal expectation, Number would (and does) look like a constructor just like UserClass or Set [08:37:16.0747] users should not regard Number as being constructible and we should not design the feature to suggest that it is [08:37:40.0205] it is true that `Number` is technically constructible but I don't care about this fact, and we should not promote it to people's attention, nor make features which support it [08:38:32.0848] i do not believe that it accepting boxed primitives - and unboxing them, ftr - is going to encourage usage of it. i strongly believe the opposite [08:38:36.0177] > <@jridgewell:matrix.org> Actually you canā€™t reference `_` in a closure (Iā€™ve never tried before): https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ab28412522dba608f6964c9a11ce600f Anyways, the value is _moved_ into the closure when you create it, so itā€™s not redeclaring a scope: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0be61d281692f23d745632011a77a86b [08:38:45.0662] > <@ljharb:matrix.org> i do not believe that it accepting boxed primitives - and unboxing them, ftr - is going to encourage usage of it. i strongly believe the opposite ... say more? [08:38:59.0429] certainly no new sites of *producing* a boxed primitive should ever be added [08:39:47.0956] if you want to use pattern matching, with the current semantics, even if you do the very unlikely thing of making a boxed primitive, you will as rapidly as possible unbox it. just like how sparse arrays are terribad, and all new stuff just papers over that by making them dense, and we do NOT throw when you have a sparse array [08:39:52.0031] I thought V8 optimized array destructuring [08:39:58.0027] * if you want to use pattern matching, with the current semantics, even if you do the very unlikely thing of making a boxed primitive, you will as rapidly as possible unbox it. just like how sparse arrays are terribad, and all new stuff just papers over that by making them dense, and we do NOT throw when you have a sparse array, we shouldn't throw or fail when you have a boxed primitive. we should just paper over it. [08:40:16.0406] * if you want to use pattern matching, with the current semantics, even if you do the very unlikely thing of making a boxed primitive, you will as rapidly as possible unbox it. just like how sparse arrays are terribad, and all new stuff just papers over that by making them dense, and we do NOT throw when you have a sparse array, we shouldn't throw or fail when you have a boxed primitive. we should just paper over it and get you into "normal primitives" as fast as possible. [08:40:20.0394] > <@ljharb:matrix.org> if you want to use pattern matching, with the current semantics, even if you do the very unlikely thing of making a boxed primitive, you will as rapidly as possible unbox it. > > just like how sparse arrays are terribad, and all new stuff just papers over that by making them dense, and we do NOT throw when you have a sparse array, we shouldn't throw or fail when you have a boxed primitive. we should just paper over it and get you into "normal primitives" as fast as possible. we don't throw when we have a sparse array because it would require a special case to do so [08:40:23.0293] > <@littledan:matrix.org> I thought V8 optimized array destructuring I remember they published an article about it several years ago [08:40:39.0111] but here you're suggesting we add a special case to _support_ the thing that people should not encounter [08:40:43.0197] I want to not do that [08:40:45.0728] we have to avoid saying "stage n concern" because of the ambiguity between this being a thing to resolve before reaching stage n, or only once stage n is reached [08:41:09.0302] if user is passing around a boxed primitive, they should expect to get an error, not for the language to accomadate them [08:41:17.0549] p sure the motivation was more "because that hurts people to no benefit" as opposed to "we don't want a special case in the spec/impl" [08:41:17.0555] * if user is passing around a boxed primitive, they should expect to get an error, not for the language to accommodate them [08:43:21.0590] I do not think that's right, but in any case, boxed primitives are more different from primitives than holey arrays are from dense arrays [08:45:06.0871] > <@littledan:matrix.org> I thought V8 optimized array destructuring Itā€™s still not nearly as fast as regular property destrutcuring [08:45:07.0739] boxed primitives exist. if they are used, then providing encouragement and ergonomics for quickly getting them unboxed is good. if they are not used, then accepting them provides no such encouragement. [08:45:38.0603] > <@haxjs:matrix.org> I remember they published an article about it several years ago https://docs.google.com/document/d/1hWb-lQW4NSG9yRpyyiAA_9Ktytd5lypLnVLhPX9vamE/edit#heading=h.9ss45aibqpw2 [08:46:03.0143] counterpoint: if they are used, _discouraging their use_ is good [08:46:10.0245] no code should accept them [08:46:20.0449] (unless it accepts other bjects, of course) [08:46:22.0764] * (unless it accepts other objects, of course) [08:50:01.0268] I am not looking forward to parsing `foo(bar(baz)) => 0` [08:50:26.0393] extractors do a good job addressing one of the points that Yulia raised a while ago, about the availability of parallel constructs inside and outside of pattern matching. [08:50:35.0142] * I am not looking forward to parsing `(bar(baz)) => 0` [08:50:41.0583] but I guess it's not that much worse than `({ bar }) => 0` [08:51:07.0274] and `async(bar(baz)) => 0` of course [08:52:06.0915] yeah by 2017 we already got up to a certain bar of annoying-to-parse, and my feeling was that extractors didn't really add anything that was so bad... but non-backtracking parsers will have some more complexity in their error tracking with this [08:52:17.0478] * yeah by 2017 we already got up to a certain bar of annoying-to-parse, and my feeling was that extractors didn't really add anything that was so much more bad... but non-backtracking parsers will have some more complexity in their error tracking with this [08:53:34.0181] it is not that much worse, it's just adding more complexity to what is already the single most complex-to-parse construct in the language [08:54:40.0825] the extractor has to be an identifier or a member access on an extractor, right? [08:55:20.0970] yeah [08:55:22.0498] It can also be e.g. `super`. [08:55:24.0763] https://tc39.es/proposal-extractors/#prod-ExtractorMemberExpression [08:55:51.0140] Ah, not `super`, just `super.foo` or `this`. [08:58:06.0590] i mean in general i agree with george but i got bad news for him on the understandability of JS wrt user code today [09:01:56.0854] That's because it's all minified on the web [09:04:06.0300] > <@jridgewell:matrix.org> @rbuckton:matrix.org Why do we lose assignment patterns with `_`? Because you can already write `[_, _] = ar` today and it has a meaning. [09:04:23.0087] Do other engines think that throwing for each failed match seems like it would be way too slow? [09:04:34.0231] > <@keith_miller:matrix.org> Do other engines think that throwing for each failed match seems like it would be way too slow? I think the concern was more around the iteration protocol [09:04:36.0727] Or did I just misunderstand the proposal? [09:04:53.0643] also engines don't have to actually do the throw [09:05:49.0137] > <@rbuckton:matrix.org> Because you can already write `[_, _] = ar` today and it has a meaning. But thatā€™s behaving exactly the way discard bindings should. [09:05:56.0870] You'd need to inline every pattern, IIUC, which doesn't seem plausible in all places (it could be virtual, it could be huge, etc) [09:05:58.0063] > <@keith_miller:matrix.org> Do other engines think that throwing for each failed match seems like it would be way too slow? While that's more of a discussion for pattern matching, Refutability is a very important mechanic for pattern matching. If you want irrefutability, you use a `default:` clause. [09:06:02.0746] We just need the ability to redeclare. [09:07:10.0884] I agree it's more of a discussion for pattern matching I don't want to get to a place where we have throwing here and have to have a totally different mechanism for pattern matching [09:07:31.0568] > <@keith_miller:matrix.org> Do other engines think that throwing for each failed match seems like it would be way too slow? it would be for a failed `match` not for a failed _clause_, to be clear - the intention for a match construct is that it always matches something [09:07:38.0449] > <@jridgewell:matrix.org> We just need the ability to redeclare. which definition would closures capture? [09:07:43.0546] > <@jridgewell:matrix.org> But thatā€™s behaving exactly the way discard bindings should. Redeclare what? if `_` isn't already declared, `[_, _] = ar` will either throw in strict mode, or introduce a global in non-strict mode. I don't think we can change that. If you want to use `_` as a discard in assignments, you do that by declaring a `var _` somewhere in the same scope. [09:08:15.0481] Or are you talking about binding patterns? [09:08:30.0368] > <@shuyuguo:matrix.org> i mean in general i agree with george but i got bad news for him on the understandability of JS wrt user code today If we're creating a brand new feature, why not aim higher? Especially if the increased power/flexibility is not required for the majority of use-cases [09:09:03.0804] > <@keith_miller:matrix.org> I agree it's more of a discussion for pattern matching I don't want to get to a place where we have throwing here and have to have a totally different mechanism for pattern matching This must throw. If `const {} = null` throws or `const [] = 1` throws, so must this. [09:09:49.0327] `match` must throw if it has no default. Since `match` is syntactic, if a `default:` clause doesn't exist you just synthetically introduce one that throws. [09:10:20.0977] > <@gkz:matrix.org> If we're creating a brand new feature, why not aim higher? Especially if the increased power/flexibility is not required for the majority of use-cases you're reading the wrong conclusion. i, as an engine person, would be very happy with no custom matchers [09:10:35.0854] i'm saying that doesn't meaningfully move the needle for the general problem of understanding user code in JS [09:11:42.0568] shu: dminor : What was the performance concern? [09:11:49.0596] the new one? [09:12:01.0551] my new one is cover grammars incur cost, as you well know [09:12:03.0289] and we don't like it [09:12:05.0423] nothing deeper than that [09:13:25.0822] > <@rbuckton:matrix.org> Redeclare what? if `_` isn't already declared, `[_, _] = ar` will either throw in strict mode, or introduce a global in non-strict mode. I don't think we can change that. If you want to use `_` as a discard in assignments, you do that by declaring a `var _` somewhere in the same scope. Oh, I was assuming `_` was already a binding in scope. Why canā€™t we make sloppy mode continue to function as is, and just not create the binding in strict? [09:13:44.0556] Regarding the `const InventoryID(customerId) = "Contoso-12345"` example, I've regularly described extractors as _unapplication_ (the Scala method is literally called `unapply()`). This dualilty is important for both data types and primitive types: ```js const InventoryID(customerID, Name) = InventoryID("12345", "Contoso"); ``` [09:15:14.0172] > <@jridgewell:matrix.org> Oh, I was assuming `_` was already a binding in scope. Why canā€™t we make sloppy mode continue to function as is, and just not create the binding in strict? That would definitely be a violation of ljharb 's concerns. I think we can only use `_` if it would otherwise be syntactically illegal. If you have ```js var _ = require("lodash"); function f() { [_, _] = [1, 2, 3]; } ``` Then you're breaking code non-locally. [09:16:18.0073] > <@rbuckton:matrix.org> That would definitely be a violation of ljharb 's concerns. I think we can only use `_` if it would otherwise be syntactically illegal. If you have > > ```js > var _ = require("lodash"); > function f() { > [_, _] = [1, 2, 3]; > } > ``` > > Then you're breaking code non-locally. We had that discussion above. Renaming the lodash import is trivial. [09:17:15.0167] In a 10,000 line file, it's easy to lose sight of the imports at the top of the file. [09:18:43.0197] oof, if you have a 10k line file you have bigger problems than a binding no longer working [09:19:22.0609] > <@rbuckton:matrix.org> Regarding the `const InventoryID(customerId) = "Contoso-12345"` example, I've regularly described extractors as _unapplication_ (the Scala method is literally called `unapply()`). This dualilty is important for both data types and primitive types: > > ```js > const InventoryID(customerID, Name) = InventoryID("12345", "Contoso"); > ``` I don't disagree that there are use-cases that would be addressed with this, just that the power required to support them (when these are the minority of use-cases, and other languages e.g. OCaml do fine without supporting this), allows completely arbitrary behavior which means that it will be more difficult to understand and analyze the main use-case, which is matching against some datatype and extracting values from it. [09:19:31.0843] Yeah, yeah. I'd love to refactor checker.ts to something more manageable, but that's still a ways out. [09:20:41.0304] > <@gkz:matrix.org> I don't disagree that there are use-cases that would be addressed with this, just that the power required to support them (when these are the minority of use-cases, and other languages e.g. OCaml do fine without supporting this), allows completely arbitrary behavior which means that it will be more difficult to understand and analyze the main use-case, which is matching against some datatype and extracting values from it. Do you mean something more like Python's `__match_args__` approach? I've discussed that with the pattern matching champions and there was fairly strong opposition. [09:21:35.0512] rbuckton: Are there any dynamically typed languages that support extractors? [09:23:06.0066] OCaml is a statically typed language, as is Rust. They have type systems to do the work for them. JS does not. It often depends on runtime evaluation of code to do similar things. Extractors is the way it is because it isn't statically typed. [09:23:41.0836] Racket, apparently? [09:24:00.0307] > <@rbuckton:matrix.org> Do you mean something more like Python's `__match_args__` approach? I've discussed that with the pattern matching champions and there was fairly strong opposition. I'm not familiar with that feature, but to explain what I mean in loose terms, you could still have a `customMatcher`-like function which can decide which values to return for extraction, but the check of "is this of this datatype" (be it instanceof or whatever) would always be run, separating these two concerns [09:24:15.0960] > <@eemeli:mozilla.org> rbuckton: Are there any dynamically typed languages that support extractors? Python has `__match_args__`, not full extractors. They'd considered them but determined they didn't have use cases. JS has private state, which Python does not, so a `__match_args__` approach is not sufficient. Other Pattern Matching champions have opinions on this as well. [09:24:49.0358] Dart recently added a pattern matching feature, and seems to work in this way: https://dart.dev/language/pattern-types#object [09:27:15.0515] > <@gkz:matrix.org> I'm not familiar with that feature, but to explain what I mean in loose terms, you could still have a `customMatcher`-like function which can decide which values to return for extraction, but the check of "is this of this datatype" (be it instanceof or whatever) would always be run, separating these two concerns In Python, you can do ```py match p: case Point(x=0, y=0): ... ``` where `Point.__match_args__` is `["x", "y"]`. It checks if `p` is a `Point`, then basically checks if `p[Point.__match_args__[0]] == 0 and p[Point.__match_args__[1]] == 0` [09:29:03.0019] There is no opportunity to run user-defined code as part of that validation. It can also only read public properties, but that's fine for Python since it does not have privacy. A `Point[Symbol.matchArgs] = ["#x", "#y"]` in JS wouldn't be able to read a private `#x` in JS. [09:32:00.0259] Is Mark Miller here this afternoon? [09:32:19.0117] my presentation is largely about refuting a past argument that he made [09:32:35.0551] Random thought: Would `@@customMatcher` as a generator be potentially more performant than returning an array or iterator? [09:35:53.0169] > <@littledan:matrix.org> my presentation is largely about refuting a past argument that he made he seems to be in the call, so I'll go ahead [09:36:13.0750] > <@eemeli:mozilla.org> Random thought: Would `@@customMatcher` as a generator be potentially more performant than returning an array or iterator? what does this question mean? a generator returns an iterator. [09:36:52.0454] > <@rbuckton:matrix.org> There is no opportunity to run user-defined code as part of that validation. It can also only read public properties, but that's fine for Python since it does not have privacy. A `Point[Symbol.matchArgs] = ["#x", "#y"]` in JS wouldn't be able to read a private `#x` in JS. you can intercept __getattr__ though! [09:37:01.0212] > <@rbuckton:matrix.org> There is no opportunity to run user-defined code as part of that validation. It can also only read public properties, but that's fine for Python since it does not have privacy. A `Point[Symbol.matchArgs] = ["#x", "#y"]` in JS wouldn't be able to read a private `#x` in JS. * you can intercept `__getattr__` though! [09:39:20.0204] littledan: I mean having ``` class Number { ... *[Symbol.customMatcher](subject) { if (typeof subject === 'number') yield subject; else if (subject instanceof Number) yield Number(subject); else return false; return true; } } ``` instead of ``` class Number { ... [Symbol.customMatcher](subject) { if (typeof subject === 'number') return [subject]; if (subject instanceof Number) return [Number(subject)]; return false; } } ``` [09:39:33.0723] (side note: Can someone update the topic to point to this meeting rather than feb) [09:41:48.0502] > <@rbuckton:matrix.org> There is no opportunity to run user-defined code as part of that validation. It can also only read public properties, but that's fine for Python since it does not have privacy. A `Point[Symbol.matchArgs] = ["#x", "#y"]` in JS wouldn't be able to read a private `#x` in JS. I like this aspect of the feature: "Whether a match succeeds or not is determined by the equivalent of an isinstance call. If the subject (shape, in the example) is not an instance of the named class (Point or Rectangle), the match fails." But my focus is on that, if you wanted to still have a `Symbol.matchArgs` function to determine the values being extracted, which can access private properties, that could be fine. This simply separates the concerns of "is this a value of this datatype" and "what values should be extracted" rather than combining them into one function and allowing both to be user-defined. [09:43:59.0496] That does not satisfy my goals and motivations. Pattern matching could do that with `x is Point and { let x, let y }`. Executing user-defined code is the whole point of the feature. [09:45:01.0819] > <@littledan:matrix.org> Is Mark Miller here this afternoon? he should be, I'll make sure he is [09:45:26.0306] To clarify, with that example it would be `x is Point(let x, let y)` - we check if `x instanceof Point`, and then get the arguments you want extracted [09:45:51.0317] If would just not allow things like using extractors on strings to extract parts of those strings [09:46:21.0729] I guess this would be similar to how both Python and Dart use these features [09:47:23.0852] I want to be able to do that with this feature. Restricting it to data types is a manufactured limitation, not a natural limitation of the syntax. [09:47:29.0999] > <@gkz:matrix.org> I guess this would be similar to how both Python and Dart use these features It is very similar to Dart. Which has been well received [09:47:46.0148] Also, `instanceof` is broken. Pattern matching is not using it. [09:48:40.0252] Sure, whatever actual check is more appropriate to use, I think the general idea I'm talking about is clear though [09:49:31.0069] > <@eemeli:mozilla.org> Random thought: Would `@@customMatcher` as a generator be potentially more performant than returning an array or iterator? No. Returning an array is the least-bad case for the iterator protocol. [09:49:41.0645] I understand the semantics you are describing. I just see no reason to introduce an artificial limitation. [09:50:05.0905] If a custom matcher is an iterator, then it is irrefutable. It always matches. [09:51:31.0611] iain: Presumably with the exception of only returning one value, and handling its destruction as a separate concern. [09:51:56.0064] The reason I pursued this feature in the first place was to support user-defined validation and transformation. That it works for datatypes is a happy benefit. [09:53:07.0709] eemeli: Yes, returning a single value would be significantly more performant [09:53:40.0749] Also, you can destructure a string, so restricting extractors to datatypes breaks that. `const { length } = "abc"` and `const [a, b, c]="abc"` are perfectly legal. I don't want extractors to be _less_ capable than `{}` and `[]`. [09:54:23.0066] Webex is again refusing to pick up my audio, hopefully I can fix it on time [09:54:48.0449] > <@iain:mozilla.org> eemeli: Yes, returning a single value would be significantly more performant And then you'd need to write e.g. `let Point([x, y]) = ...` to get the same effect, if you need more than one value out of the extractor. [09:55:35.0773] > <@rbuckton:matrix.org> I understand the semantics you are describing. I just see no reason to introduce an artificial limitation. It seems like languages like Dart, Python, OCaml, Rust all have this limitation. And the reason is what I described before, there is a trade-off between the flexibility this feature provides, and making code easy to understand (for both humans and tooling) [09:55:43.0411] Besides, writing an extractor to handle a non-datatype input is more complex than the default implementation for a data type. In the pattern matching proposal, `class C {}` will have a default `[Symbol.customMatcher]` that performs a brand check and returns the subject. You get it for free unless you want to do the more complex things. [09:57:25.0552] And ideally, ADT enums would have a default implementation that matched their definition, i.e.: ```js enum Message { Write(text), Move({ x, y }) } const Message.Write(text) = msg1; const Message.Move({ x, y }) = msg2; ``` You would have to manually write a `[Symbol.customMatcher]` method to override this behavior, so it is an opt-in mechanism. [09:58:04.0574] > <@gkz:matrix.org> It seems like languages like Dart, Python, OCaml, Rust all have this limitation. And the reason is what I described before, there is a trade-off between the flexibility this feature provides, and making code easy to understand (for both humans and tooling) C# and Scala do not have this limitation. Scala has `unapply`, C# has `Deconstruct`. [09:59:27.0340] Yeah, given that there are trade-offs and different languages have made different design choices regarding this trade-off, it seems worth discussing at least! [10:00:04.0886] Slides for my Array.isTemplateObject presentation: https://docs.google.com/presentation/d/1LTlzpboYwKxRwigATcFYEh06CIbZvOvmFdPzkNn7vJI/edit#slide=id.g2cae2397581_0_66 [10:01:49.0230] > <@gkz:matrix.org> Yeah, given that there are trade-offs and different languages have made different design choices regarding this trade-off, it seems worth discussing at least! A good place to continue this discussion would be in the the #tc39-pattern-matching:matrix.org room [10:02:07.0820] Thanks, will join now! [10:08:49.0839] > <@rbuckton:matrix.org> Besides, writing an extractor to handle a non-datatype input is more complex than the default implementation for a data type. In the pattern matching proposal, `class C {}` will have a default `[Symbol.customMatcher]` that performs a brand check and returns the subject. You get it for free unless you want to do the more complex things. Is there any plan to move/add those default matchers to extractors? [10:11:32.0925] The exact semantics need to match between destructuring and pattern matching, so yes, most likely. Currently it's being managed as a cross-cutting concern between the two proposals. If extractors in destructuring gets Stage 2, but pattern matching is held back, then we would likely need to move over the default semantics. [10:18:08.0592] i don't think stage 2 is the time to separate them, it'd be if extractors is ready for 2.7, and pattern matching isn't stage 2 and also is content with the design constraints that advancing extractors would cause [10:30:59.0581] ok so why is this stage 3 instead of a normative PR? the discussion was hard to follow [10:31:12.0175] it should return a spec enum or a string [10:31:36.0350] * i don't think stage 2 is the time to separate them, it'd be if extractors is ready for 2.7, and if pattern matching isn't stage 2 and also is content with the design constraints that advancing extractors would cause [10:31:37.0039] > <@ljharb:matrix.org> ok so why is this stage 3 instead of a normative PR? the discussion was hard to follow I don't think we really discussed that, but is there a problem with considering it Stage 3? [10:31:44.0110] we can still reconsider [10:31:54.0761] just more ceremony than we probably need [10:31:59.0474] IMO it'd be valid either way [10:33:24.0511] +1 would be good to document where the dividing line is. I agree with Jordan in that I would've preferred this to be a needs-consensus PR [10:33:37.0830] Nicolo's answer seemed good: the stage process let us develop this proposal and build consensus incrementally [10:34:19.0883] was this one even a proposal before today? [10:34:35.0596] > <@ljharb:matrix.org> was this one even a proposal before today? Yes, "dynamic code brand checks", stage 1 [10:34:44.0319] aha, thanks [10:35:17.0039] i'm not sure being stage 1 for many years, and suddenly getting revived and jumping straight to stage 3, justifies it being a proposal vs a PR, but it really doesn't matter much :-) [10:36:03.0082] To be honest I spent the last two weeks swinging between "stage 3" and "normative PR", but given that we are changing behavior of `eval()` (and dropping the guarantee that all objects are returned as-is), I preferred being more conservative [10:36:14.0350] Implementations are in progress, so this will be ready for stage 4 soon [10:47:41.0255] šŸ¤” could you data URI an iframe and then pull the "literal" template out of it? [10:48:23.0148] @bakkot:matrix.org ^ [10:48:33.0297] no, those are cross-origin [10:48:50.0809] okay good [10:49:22.0134] I can never remember how data URIs and file/loopback URIs can interact with web pages [10:49:41.0846] data URIs are opaque (=unique) origins in all contexts I am pretty sure [10:50:28.0970] that's good, and I'm sure I will forget before the next time I need that piece of information [11:00:18.0692] I have to drop for a bit but I'm happy with this proposal with either cross-realm or same-realm semantics [11:09:21.0737] is this only useful for bundlers? if so, can't they start using a non-standard directive? [11:13:08.0262] I kinda share danielrosenwasser 's composability concern [11:13:29.0518] nicolo-ribaudo: I believe "adding TLA currently considered to be a semver-major change" should be true... [11:14:37.0136] > <@michaelficarra:matrix.org> is this only useful for bundlers? if so, can't they start using a non-standard directive? i'd appreciate your putting this on queue [11:14:58.0354] adding TLA is always a breaking change. [11:15:00.0648] if other people share daniel r's view that this is a dev-time thing, that seems to segue naturally into tools doing better here [11:15:04.0043] * adding TLA is always a breaking change, with or without this proposal [11:15:06.0292] > <@michaelficarra:matrix.org> is this only useful for bundlers? if so, can't they start using a non-standard directive? It's also useful if someone want to make it clear some code is rely on sync semantic. [11:15:08.0285] feel free to take it from me @shuyuguo:matrix.org [11:15:30.0193] I do feel like it's a dev time thing though [11:16:34.0467] Yeah feels like a dev time issue [11:16:46.0934] imo we shouldn't have allowed TLA to be used in a statically imported module in the first place [11:17:21.0447] Does this proposal have a spec? [11:17:25.0230] * imo we shouldn't have allowed static import of a module using TLA in the first place [11:17:36.0900] I find the "you'll get an error anyway" argument compelling [11:17:38.0165] > <@littledan:matrix.org> I kinda share danielrosenwasser 's composability concern The root cause is TLA is already lose composability ... [11:19:00.0379] To be honest, I really feel this proposal is just a patch for TLA footguns... [11:19:38.0198] re semver: What if we standardized semver in Ecma? Darcy Clarke is interested in this, with a better specification than exists currently (including Node semver ecosystem reality). We could do it in a new TC in TC39, or maybe a TC39 TG. Who's in? Eventually maybe it'd make sense for JavaScript to have a small built-in library for working with semver values, once we have the core defined (this is an extremely popular npm package). [11:19:46.0914] * re semver: What if we standardized semver in Ecma? Darcy Clarke is interested in this, with a better specification than exists currently (including Node semver ecosystem reality). We could do it in a new TC in Ecma, or maybe a TC39 TG. Who's in? Eventually maybe it'd make sense for JavaScript to have a small built-in library for working with semver values, once we have the core defined (this is an extremely popular npm package). [11:20:29.0432] I wonder if the service worker API could be updated to not have the late-listener issue [11:20:49.0568] > <@littledan:matrix.org> re semver: What if we standardized semver in Ecma? Darcy Clarke is interested in this, with a better specification than exists currently (including Node semver ecosystem reality). We could do it in a new TC in Ecma, or maybe a TC39 TG. Who's in? Eventually maybe it'd make sense for JavaScript to have a small built-in library for working with semver values, once we have the core defined (this is an extremely popular npm package). I spent enough time in the past month fighting with semver implementations that don't follow the community spec that I'd love to do it [11:22:28.0855] > <@ljharb:matrix.org> imo we shouldn't have allowed static import of a module using TLA in the first place At least not by default (it should only be allowed with