From b2f00f74e34fd93dfdd1275b9d0d7ac5e9dbf29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aki=20=F0=9F=8C=B9?= Date: Mon, 10 Aug 2026 14:11:50 +0200 Subject: [PATCH] 2026 July transcript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Aki 🌹 Co-authored-by: Michael Ficarra --- meetings/2026-07/july-20.md | 851 ++++++++++++++++++++++++++ meetings/2026-07/july-21.md | 1100 +++++++++++++++++++++++++++++++++ meetings/2026-07/july-22.md | 1145 +++++++++++++++++++++++++++++++++++ 3 files changed, 3096 insertions(+) create mode 100644 meetings/2026-07/july-20.md create mode 100644 meetings/2026-07/july-21.md create mode 100644 meetings/2026-07/july-22.md diff --git a/meetings/2026-07/july-20.md b/meetings/2026-07/july-20.md new file mode 100644 index 00000000..d7cca884 --- /dev/null +++ b/meetings/2026-07/july-20.md @@ -0,0 +1,851 @@ +# 115th TC39 Meeting + +Day One—20 July 2026 + +**Attendees:** + +| Name | Abbreviation | Organization | +|---------------------|--------------|----------------------| +| Dmitry Makhnev | DJM | JetBrains | +| Waldemar Horwat | WH | Invited Expert | +| Bradford C. Smith | BSH | Google | +| Lea Verou | LVU | OpenJS | +| Keith Miller | KM | Apple Inc | +| Ashley Claymore | ACE | Bloomberg | +| Aki Rose Braun | AKI | Ecma International | +| Linus Groh | LGH | Bloomberg | +| Istvan Sebestyen | IS | Ecma International | +| Nicolò Ribaudo | NRO | Igalia | +| Philip Chimento | PFC | Igalia | +| Chip Morningstar | CM | Consensys | +| Ujjwal Sharma | USA | Igalia | +| Eemeli Aro | EAO | Mozilla | +| Caio Lima | CLA | Igalia | +| Olivier Flückiger | OFR | Google | +| Nikolaos Papaspyrou | NPU | Google | +| Chris de Almeida | CDA | IBM | +| Richard Gibson | RGN | Agoric | +| James Snell | JSL | Cloudflare | +| Guy Bedford | GB | Cloudflare | +| Devin Rousso | DRO | Invited Expert | +| Samina Husain | SHN | Ecma International | +| Luna Pfeiffer | LPR | Invited Expert | +| Chip Morningstar | CM | Consensys (MetaMask) | +| Dan Minor | DLM | Mozilla Foundation | +| Justin Ridgewell | JRL | Google | +| Kevin Gibbons | KG | Invited Expert | +| Keith Miller | KM | Apple | +| Michael Ficarra | MF | F5 | +| Mark S. Miller | MM | Agoric | +| Nicolò Ribaudo | NRO | Igalia | +| Peter Klecha | PKA | Bloomberg LP | +| Stephen Hicks | SHS | Google | +| Ujjwal Sharma | USA | Igalia | + +## Opening & Welcome + +Presenter: Chris de Almeida (CDA) + +CDA: Excellent. All right, welcome to the 115th meeting of TC39. This is your chair group, Rob, Ujwal, Chris (myself), and Justin, Daniel Minor, and Daniel Rosenwasser as facilitators. Rob is on PTO, so he will not be joining us on this occasion, but I think the rest of us are mostly here, myself, Ujwal, and whoever else is currently in the meeting. You— this says make sure to sign in, but, almost surely you must have if you got the meeting link. But if you got the meeting link, via some other means, please do complete the sign-in form that helps us track who was actually here. And that is available on the reflector issue for the meeting. Just a reminder, we follow our code of conduct, which TL;DR of which is be excellent to each other. Our schedule is as follows. For the next 4 days, meeting starts at 10:00 local time, then we break for lunch at noon, and, for an hour, and then we return for 2 additional hours. Communication tools: hopefully folks are familiar already with TCQ, and we have got the screenshots updated to reflect the new, fancy TCQ, courtesy of Michael Ficarra and other contributors. This is the— oh, this is an old screenshot, but it's more or less the same. So this is the participant view of TCQ. You have your buttons there to, state what sort of, reply you have to things, whether it's a new topic or reply to the current topic. A clarifying question or a point of order. A gentle reminder on point of order, feel free to kind of abruptly interrupt without waiting for, someone to bring attention to your point of order. Those tend to be things that we need attention on immediately. We use matrix for chat. The primary channel during plenary meetings will be the second one you see there, TC39 delegates. That top link is for the TC39 space itself. If you join the space, you will see all of the different TC39 channels without having to find them, cherry-picking. The Temporal dead zone channel is used for off-topic things, so please keep, off-topic and banter, in that channel. And so the TC39 delegates channel is for, real business. Just a reminder on the IPR policy, which applies for the member delegates. Invited experts, any invited experts that, I believe everybody's got, signed the RFTG form as that's now a condition of becoming an invited expert. And yeah, details are in the contributing MD. Make sure you have reviewed this. I believe we may have some observers attending in this meeting, and a reminder that observers are welcome to observe, but do not have voice. The dialogue is being transcribed live. We always ask for note helpers to assist in making corrections and attributions. And, as Aki, I'm sure will remind us, many times during the meeting, to please add your summaries and conclusions. And those are different things. Which, Aki will, will, will be happy to reiterate, later. And just a reminder that the transcript that's being prepared will eventually be posted on GitHub. You may edit this at any time during the meeting, in Google Docs for accuracy, including deleting comments which you do not wish to appear. You may also request corrections or deletions after the fact by editing the Google Doc in the first 2 weeks after the meeting or subsequently by making a PR in the notes repository or by contacting the chairs. Please help with notes. We really rely on our note helpers, to make these smaller corrections and attributions. And for those helping with the notes, it always helps to have the text file with all of the delegates' 3-letter acronyms, available in a browser tab for quick reference. + +KG: Before we move on, one note there is that we are using a even newer transcription model than we were at the last meeting. And I do think it is, performing perhaps marginally better. So hopefully the experience of being a note, taker/corrector, will be even easier at this meeting than it previous ones. Although please feel free to ping me if it is not. But, yeah. + +CDA: Yep. Thank you, Kevin. Yeah, definitely, it also would be helpful feedback for anyone who has taken notes, you know, for example, at the last meeting, if you do notice, any differences, good or bad, that would be, helpful information as well. All right. Our next meeting will be an in-person/hybrid meeting, once again in Tokyo. We are returning to the Sony Interactive Entertainment office, in Shinagawa, in late September to early October. We did complete the interest survey for that, and deemed there was enough interest to proceed with the in-person meeting, keep an eye out. There should be a, a, for folks that completed the in-person, survey, you will still need to complete the new form that's coming, very soon, for actual in-person registration. So keep an eye out for that. And we will go through our regular, agenda, first, of course, asking for note-takers and then some other items here. Let me just take a quick look at TCQ, make sure I'm not missing anything. Because I think I do not have on here the approval of the previous minutes. So let's do that now. Actually, let's do that once we have, note-takers. So let's do that. Do we have any volunteers to help with the notes? We are looking for 2. Two individuals. + +ACE: I'll help you out. + +CDA: With the notes for this session. Who was that? + +ACE: Ashley. + +CDA: Ashley, thank you so much. Can we get one additional person to help with the notes? We can proceed with the meeting once we have one additional volunteer. Still looking for one person to help out with notes. Which entails making small corrections, and attributions. As KG noted, the new model should make the number of corrections you need to make, even less than before. Which was already a small amount. Maybe we could get somebody to volunteer for the first hour. + +NRO: I can volunteer. At least for the first hour. I have another meeting in 1 hour, though, so I need to step out for 20 minutes. + +CDA: Okay. Thank you so much, NRO. All right. + +CDA: So, first order of business: approval of the previous meeting minutes. Are there any objections to approving? I believe we already merged those. Yep. The PR has been merged. In the notes repo. All right. No objections. We have approved the previous minutes. Next up, adoption of the current agenda. Is there any objection to the agenda as it stands? Okay. The agenda is approved. I will cease to share my screen at this point. NRO mentions there's a 3-weeks-long meeting in Tokyo. Did we have a bad-? It should be the first 29 September through the 1st, not the 21st. I as much as people like the Tokyo meetings, probably that would be, too much plenary for anybody. Noting here that, typically we would start with the Secretary's report, Samina has a conflict and so that will be coming later in the meeting. So the first topic is editors' reports. Michael, are you there? + +## Editors Report + +Presenter: Michael Ficarra (MF) + +* [slides](https://docs.google.com/presentation/d/1uCpu8x8Fj0cZ41eQpygsS5uuX-xjiFKW0mxq6z2hTZQ) + +MF: This is the editors' update. We've had 3 major normative changes since the last meeting. We landed joint iteration, which includes `Iterator.zip` and `Iterator.zipKeyed`. We landed `Atomics.pause`. And also explicit resource management, which includes `using`, `await using`, that kind of stuff. Also, we had a small follow-up because something got dropped in a rebase, so explicit resource management is split across those 2 PRs. + +MF: Notable editorial changes. We did a lot of work on the aliases used in the spec. We are now consistently either abbreviating or not abbreviating certain terms. So you won't see, like, `attr` and `attribute`, they’ll be, just one of those. We also made sure that we use the same alias for the same concept used in many different places, so you won't have, like, `s` and `sym` and `symbol`, all talking about a Symbol. So yeah, yeah, just small consistency things that I think are going to improve the quality of the spec little by little. You can look at the editorial conventions document to see what our policy is there now for naming. If you maintain a proposal, it'd be nice if you, you know, update accordingly. Otherwise, we'll just have to do it during review, when it's trying to land after stage 4. We also notably renamed these internal slots on private elements and property descriptors to `[[Getter]]` or `[[Setter]]`, from `[[Get]` and `[[Set]]`, because that overlapped with a concrete method name, and now you can just Ctrl+F on the spec for getter and setter, and it brings you to all the occurrences of exactly that thing, which is working toward our goal of improving the spec navigation when it comes to, like, concrete methods and internal methods and stuff. And here, in this case, internal slots. + +MF: We also added a feature to ecmarkup for preserving your multipage preference, which means, like, whether you view the spec as single-page or multipage. By default, it will just have no preference, which means you can just use the `m` keyboard shortcut to toggle between single-page and multipage viewing. If you do have a preference, you can go into the keyboard shortcut help dialog with `?` and select one of those preferences. So if you select always single-page, if you receive a link to the multipage document, it will navigate you to the single-page immediately. You know, and the other way around for always multipage. Most people probably want to leave it on no preference, and you won't notice a difference. + +MF: We've updated the upcoming and planned editorial work. I think 3912 is just about to land, so maybe we'll be removing that one shortly. You can, as always, track what we are working on the GitHub project. Link's at the bottom. That's probably the best way to know, in more fine-grained issue level stuff, what we're going to be working on next. And that's it. Any questions? + +CDA: Nothing on the queue. + +### Speaker’s summary of Key Points + +* ECMA-262 editors landed 3 normative changes, some editorial changes, and an ecmarkup feature for a sticky multipage preference + +## ECMA402 Update + +Presenter: Ujjwal Sharma (USA) + +* slides presented. link TODO + +USA: Hi, everyone. And likewise, I'll do a quick status update for ECMA 402. Do let you all know how we're doing on the editorial side of things, and make sure that you're not disoriented for the upcoming, normative, stuff. So, first, let's go over the things that we've merged since the last 2 months. + +USA: An unintentional normative change that was reverted. GetCalendars specifically. + +USA: There was another PR by RGN. Basically, making editorial changes and improving the specification of this algorithm. Specifically to break it into steps. And then there's simple changes like this that just fixed issues. So the most serious one of these is clearly the one that is normative, but it was discussed in TG2, and we agreed that this doesn't need consensus. Since it's an unintentional normative change, that's being reverted. And yeah, this one is just renaming. Then we have a FormatDateTimePattern, mathematical comparison, changes. These are all, essentially tiny changes and small editorial things, such as in this case where we use a different comparison operator, so yeah, that changes that that shows you the kind of changes that we've been making, like sync alias names, and other editorial fixes. This I suppose is too tiny. + +USA: Switching from RangeError to TypeError, for matching conventions that we have elsewhere. As well as, yeah, meta or editorial meta changes like this. + +USA: This is the issue that was discussed in the last meeting as well. I briefly mentioned it as having just opened up and now it's merged. Actually, it was merged right during the last meeting. + +USA: And there's a few open issues as well. In a similar vein, simple editorial things. And yeah, thanks, RGN, for always keeping up with these. This one is a bit more involved. Some of these could actually really use somebody reviewing them, so help us out if you can. And that's it for 402. + +## ECMA404 Update + +Presenter: Chip Morningstar (CM) + +* No slides + +CM: Where I am in California, it is summer, and as you all know, JSON does not change during the summer. Of course, it also doesn't change during the winter or the spring or the autumn, so don't think that you can get away with any shenanigans just by going to the Southern Hemisphere. + +CDA: Excellent. Don't forget to include your summary and conclusions. + +## Test262 Update + +Presenter: Philip Chimento (PFC) + +* No slides + +PFC: We did not previously have a presenter for this, but I can volunteer to present it. The main thing I'd like to share is that KG is now a maintainer. Big thanks to KG. And, in our last meeting, we noted that there are several PRs for stage 3 features that are close to landing, iterator chunking, iterator includes, regex buffer boundaries, iterator join, and immutable ArrayBuffer. Just need some last review to get these over the line. I had hoped to review immutable ArrayBuffer and `Iterator.join` myself before the meeting started, but I will probably get to it this week. And I believe that is all for our update. I will paste the summary and conclusion into the notes. + +CDA: Great. Thank you. + +### Speaker's Summary of Key Points + +* KG volunteered as a maintainer, big thanks! +* Pull requests for stage 3 features are close to landing: Iterator chunking, Iterator includes, RegExp buffer boundaries, `Iterator.join`, immutable ArrayBuffer + +## TG3 (Security) Update + +Presenter: Chris de Almeida (CDA) + +* No slides + +CDA: You know what I always say. We have been meeting regularly, although attendance has been rather low lately. I think just because of the summer and people on PTO, etc. But we continue to discuss the security implications of proposals at various stages. Please join us weekly on Wednesdays if you are interested. + +## TG4 (Source Maps) update + +Presenter: Nicolò Ribaudo (NRO) + +* [slides](https://docs.google.com/presentation/d/1xIKd9vSR01B3Nx2pSfR2XegZbAxVbKE-m4Pro0tji2s) + +NRO: Okay. Teach for a part. As always, we're working on two main proposals. The first of them range mappings. It just reached stage 3 in our TG4 process, just a reminder, TG4 process is very similar to main TG1 process. We also already have one merge implementation WebKit dev tool, so have in progress implementation in source map generators. So tools that generate just code and source maps. One big difference between our two processes is that we need four implementations for stage 4. So to consumers, like, to debugging tools and to generators. And the other proposal we're working on, scopes, it has already been stage 3 for a while, but there's been some recent involvement from people working on Dart compiler, with multiple suggestions on how to optimize the format. So that it's faster to the code and leads to lower memory usage. So even though this stage 3, there are multiple implementations ongoing, especially in consumer tools. It has been a little bit more in flux recently. So if any of you is, like, looking into implementations, please just be a little more careful. + +NRO: That's it. As always, work keeps going well in TG4, and you're all welcome to join our monthly calls. + +Great. Thank you. + +## TG5 Update + +Presenter: Michal Ficarra (MF) + +* [slides](https://docs.google.com/presentation/d/1gJ-bB4C5UdXmssiEsK2gnMdm1janUYfqelY_stdMD04) + +MF: Okay. Yeah, this is the TG5 status update. Usually MBH gives this, but he is on holiday now, so I'll be giving it. We are both conveners though. You should come to our monthly meetings. The most recent one was from DJM on a tool that he built for internal use at JetBrains, tracking proposals and sharing updates internally, within the company. Remember that because this will come up later today in one of my later topics. Also some upcoming stuff. We have a formalization of Temporal this month, and next month we have a compiler from TypeScript to Lean, so check those out. Our meetings are every month on the last Wednesday of the month. We also have an in-person workshop, again, at the next in-person meeting in Tokyo. This one is at Shibaura Institute of Technology in Tokyo. So we'll have the agenda for that up soon, as soon as we get things finalized. Remember, we like to present a couple of topics from TC39 delegates and a couple of topics from local PL researchers at a local university, so we'll have some topics from them as well. + +MF: Also, we want to remind you there's a workshop—that MBH, YSV, and I are organizing a workshop at SPLASH this year. SPLASH is in Oakland, right after the TC39 meeting in Tokyo. So a lot of you are literally taking that flight. Please, please come to our workshop on programming language standardization and specification. We have some great submissions. Submissions are still open until August 1st, so feel free to also submit stuff yourself if you care about programming language standards or programming language specification. We're really excited about our 2026 program. + +### Speaker’s summary of Key Points + +* TG5 meetings and workshops are great, you should go +* PLSS is going to be great, you should go + +## Needs-consensus PR: Support BigInt coercion of integers expressed as exponential-notation strings + +Presenter: Richard Gibson (RGN) + +* [PR](https://github.com/tc39/ecma262/pull/3857) +* [slides](https://docs.google.com/presentation/d/1kPYmR8LkoV4AZpSM_ezHCyR2givdk9OiQlwiXHdah4M/edit?usp=drive_link) + +RGN: All right. So this is our second time discussing this PR. The first time, I think, was at the last meeting. And we got into a little bit of a discussion, but basically just didn't have enough time to cover it. So hopefully this meeting will have some kind of conclusion one way or the other. What we're looking at is coercion of exponential notation strings, with `BigInt` called as a function. Which right now is invalid. + +RGN: So structuring this as if it were a staged proposal, the problem statement we're dealing with is that exponential notation is useful for dealing with big integers of arbitrary size, but is unavailable for direct use in defining a bigint. It can be done manually, by accepting strings and breaking them into pieces for integer part, fractional part, exponential part. But that is cumbersome and tedious and even though this particular issue was discovered in amount, which wants to store string values in exponential notation as a canonical form, it also comes up elsewhere, the most obvious place, is in parsing JSON with source text access, because exponential notation is a valid way to represent numbers in JSON. This table shows the landscape of numeric parsing currently in ECMA-262, and we’re talking about the lower right cell, an example of what's currently invalid that this PR is trying to make valid. Which is `BigInt` called as a function with a string argument where that string is itself in exponential notation. + +RGN: But to go back now and describe the rest of the table, this is how we deal with the parts of input that are not theirselves decimal digits, for number literal, bigint literal, Number(string), and BigInt(string). Leading and trailing whitespace is allowed everywhere although it's not really applicable in the literal columns because it doesn't show up as part of the token. Radix prefixes like `0x`, `0b`, or `0o` to indicate hexadecimal/binary/octal are valid in all of these places, and changes interpretation to use the appropriate base. Underscore separators are valid in literals, whether number or bigint, and are not valid in strings passed to either function. And then the relevant concern here, decimals and exponents are valid for number, whether literal or a string parsing function, are not valid for bigint in either case. And the convenience that we're lacking proposes to introduce alignment between BigInt(string) and Number(string) for such input, rather than BigInt(string) with bigint literals. So we’d still have inconsistency in this space, but aligning the functions rather than the literals. + +RGN: One of the points raised in the discussion on this pull request is what do other languages look like? And it turns out that there's not a lot of consistency across them, but just to share what's going on, we basically have a range of behaviors. Python actually is the most liberal; they're the only one that basically treats the input for parsing big integers the same way as they do their literals. So you can have wrapping whitespace if you want, radix prefixes, and separators (which are a bit unusual), but you do not get the parsing of exponents. On the other end of the spectrum, you have some very restrictive languages. Go, for instance, doesn't support whitespace, nor does Java for that matter. Some of them parameterize the base to be used for parsing. .NET is particularly interesting in that they're the only one that does support parsing of exponents. But everything with .NET manifests as an option. So they're also the only language where you can prohibit the leading plus or minus sign indicator. And not presented in the table here, but it turns out that ECMA-262 is unique; all of these other languages truncate when coercing a non-integer number into an integer, but we throw an exception in that case. + +RGN: Regardless, focusing on the pull request itself + +_switch from slides to specification_ + +RGN: So the change to our specification is essentially that in StringToBigInt, this internal operation that is used by BigInt(string), rather than parsing a new grammar dedicated to that operation, we use the existing StringNumericLiteral and constrain after the fact to integer MV results \[… …] If we decide to commit to this pull request, and make that normative change. It can be rejected; hopefully not. Or we could alternatively promote it into a staged proposal in which case I would probably seek stage 1 at this meeting and then prepare some things for the future to go on to later advancement. But with that, I am ready for TCQ. + +MF: Okay. I'm not in love with this proposal. I think—so the main thing that this is adding is the exponential notation being accepted by a `BigInt` constructor. And I really don't see why people would want to express bigints in exponential notation. I think that that's a really awkward way to do that, especially because exponential notation is a format that allows for expressing many things that are non-integral. And it just—we accept many different forms that end up being integral. So we have like a `1.5`, but it's `e2`, that ends up being integral. So why are we expressing it with a decimal on the left anyway? And then we have like `1000e-1`, and that is still integral. So it's like, it's still accepted even with like a negative exponent there. That's just like, I don't think that that's something we should add to this API at least. And I saw it in your table, like no other languages even support that. There was an option in .NET for doing so. I think that's probably just because they're being incredibly generic about it. Yeah, so that's it. + +RGN: So one of the bits of text you had in your topic was the possibility of a `BigInt.parse`. This is a thing that I didn't add to the slide, but I've also considered it. And probably we should keep that in mind for later parts of the discussion as well. Did you want to comment specifically on it before we move on? + +MF: That's my next topic, actually. + +KM: So I guess one thing that's kind of odd to me is that if we're expecting the output to always be in integer anyway, why allow the decimal point in the like string literal? It seems like if it's going to be an integer either way, you can always express it without the decimal point and just change the exponential. So it seems like I would find it confusing to express it that way. I don't necessarily have a problem with it existing that you can write a decimal, but it does seem like it adds to the confusion potential for people without adding any value. + +RGN: The short answer there is that essentially the canonical form is to have a single digit to the left of the decimal point. And everything else to the right of the decimal point with the exponent scaled appropriately—and the reason for that is that it indicates exactly which digits are significant. So `1.000e3` is not the same as `1e3`, and `1000` is ambiguous between the two. + +KM: But we don't have any way to handle significant digits in—like we don't retain the significant digits anywhere. So that information lost the second passage. + +RGN: But Amount has precisely that need. Which is what called this to our attention in the first place. + +KM: It seems like—I mean, I guess I could see the idea for the parity—I don't know, like overly concerned about it. But it doesn't seem like it's a strict necessity. I mean, I could see why maybe it would be slightly convenient, potentially, to pass the same value into both places. It doesn't seem strictly necessary, though. If we're worried about confusion, + +RGN: To put a fine point on it, if we didn't allow the decimal point in this notation, then the change would not satisfy the issue that was discovered by Amount. And Amount would have to go a different way in order to address it, which means that essentially this would probably not be worth pursuing on its own merits given that the motivating problem still remains open. + +KM: But wouldn't amount still have to like figure out how to extract the number of significant digits? Like it's still going to have to do some calculation for like trailing zeros and other things from the string. So it's not like you can just pass the string in and magically you have the. + +RGN: Oh, the inner machinery of Amount is fine. It's that the API surface exposed to user code includes the canonical form, which can no longer convert to `BigInt` because it includes the decimal point. So it would be a source of user friction. + +KM: I see. So you can't just take the—the building blocks don't connect. But you can't—you can't take the pieces and connect them together. + +RGN: Right. + +KM: Okay. I see. + +MF: I feel like the utility of the `BigInt` constructor is in the transitional period when browsers weren't known to support BigInt literal notation, and it could be a way to write a bigint without depending on that support. And, adding more accepted notation without accepting that also in the syntax seems like not a great direction to go. I don't necessarily support adding exponential notation BigInt literals, but if we were to add the support in the BigInt constructor, you know, that inconsistency would be undesirable. I would prefer to see a path where if we really do find value in this, we pursue a proposal for a separate method on BigInt, something like `parse`. We have `Number.parseInt`, `Number.parseFloat`. We can have `BigInt.parse` or `parseInt`, whatever you want to call it. And that can support all the notation we want. We could even include the separators, whatever we want. I think I'd rather just have the constructor stay kind of as it is supporting the absolute minimum. + +RGN: Yeah. This is a position that resonates with me, as well. It's kind of strange that we already have separators valid in the literals, but not in dynamic parsing. But we've lived with it. That hasn't been a significant source of friction in BigInt, or in Number for that matter. I am explicitly not proposing that we change the syntax of BigInt literals here. That would actually be, as far as I can tell, a completely novel behavior for ECMA 262 if we were to pursue it. I couldn't find any language that allowed for exponential notation in arbitrary precision integer literals. And I don't particularly want that for our language. But a parse function makes a lot of sense to me. You know, as I said, it's not in the slides but is something that I considered as well. And if we do feel good about it, probably the way to pursue that is as a staged proposal. And I would feel good about it. + +KM: Is there a reason that we are returning undefined rather than just like throwing an error? It feels like if there's not an integer, it might make more sense to throw all those ones says that it does. So I'm confused. But. + +RGN: The diff showed only this StringToBigInt operation. But the consumer of it does throw. + +KG: Oh, I see. Okay. Sorry. I misunderstood them. + +RGN: It doesn't back map to a syntax error. + +KM: Yeah. Cool. Cool. Okay. I retract my comment. + +PFC: I just wanted to note that working on dates and times, I have had many uses for this. I've often wished that you could just put in, 1e6 or 1e9 or whatever, whether that's in a BigInt literal syntax or as a string in the BigInt constructor, because it's tedious to type out all those zeros. I support at least solving that problem for JavaScript programmers. + +RGN: It also comes up in dealing with really small atomic units for currency. Like if this existed I think it would see use. + +EAO: Given that we already have `BigInt` as a function accepting a string argument and then parsing a numerical integer value from that string, I would find it very surprising if we introduced a `BigInt.parse` function that also accepted a string as its numerical argument and then would actually perform a different parsing on that string compared to just using `BigInt` as a function. + +WH: +1 to EAO. + +JRL: RGN, do you want to respond? + +RGN: No. I think we can move on. + +MF: Yeah. I think that this needs-consensus PR seems to be a little bit less straightforward and obvious than it seems RGN had hoped. And because of that, I feel like this is probably worth doing as—following the stage process. + +RGN: Yeah. The shape of this discussion has me agreeing with that as well. + +OFR: Yeah. Just wanted to make a quick observation. It seems like we have now two use cases that were mentioned so far. So one is for Amount where basically the argument is a runtime value and then the second use case would be more like a literal and it seems that we need to look at these differently probably because if it's a literal then probably the literal notations should support it if that's the intended use case. And if Amount is the intended use case, there I would just add one thing that maybe we should wait for the proposal to reach the final shape before we add things to the language to support like potential future uses. If we don't know yet how exactly the proposal will end up looking. + +RGN: Okay. So if I understand correctly, you're drawing a distinction between essentially the static versus dynamic use cases for getting at a BigInt from something that starts as exponential. + +OFR: Yeah. I think like especially for the literal case, I think it would be an antipattern to not write the literal and instead write a string and then have it parse so that would be an antipattern. So that would be a bad use case, I think. + +RGN: But you would be comfortable with introducing new literal syntax to support that in place of big in call this function? + +OFR: Oh, I don't know if I would be comfortable with that. I'm just noting that if we want like if this is an intended use case, then I don't think that this PR is a good match for this use case. So I would say we have currently heard two use cases. One is big int literals for that. I don't think that the PR is a good solution. And the second use case is a proposal that is still in flight where I would say let's wait until the proposal has the final shape. So that's my point. + +RGN: That's okay, I hear that. But if the literal use case is not achieved by calling a function, then the only other option is a syntax change. + +OFR: Yeah. Exactly. And but that might be a problem of this pull request that it basically encourages you to write to convert something which was a literal into a string that is then parsed in I don't know where it would be parsed. Maybe it can be optimized, maybe not. Who knows? But what I'm saying is like you basically you convert. A static literal into a string that needs to be parsed. And that's not like I would not say that this is a good conversion to encourage people to do. + +RGN: Okay. I'm just noting that the possibility space is very small then. Like either that use case is not supported or it's supported by something like this. Either BigInt constructor or `BigInt.parse`, whatever. Or it's syntax and so it's very relevant whether a syntax change is considered acceptable or not when we get into this. I'm not proposing to resolve that today, but just making an observation. + +OFR: Yeah. I guess I agree. + +RGN: Okay. Seeing an empty queue and taking into consideration the way this discussion went, I would like to request stage one for Bigint from exponential notation with the problem statement in front of everyone right now \[“Exponential notation is useful for dealing with big integers, but unavailable for direct use in defining a bigint.”]. + +JRL: So we're getting explicit support from WH, from MF, from JSL now. + +CDA: Oh, I did not realize I was on mute. Yes. I was reading the problem statement for the record, which is that exponential notation is useful for dealing with big integers but unavailable for direct use in defining a BigInt. + +CDA: And on the queue, as. I will repeat their support from WH, from MF, and from JSL now. Any objections to stage 1 for this problem statement? All right. Looks like you have stage one. Thank you, RGN. Do not forget to follow up with your key points summary conclusions. + +### Speaker's Summary of Key Points + +* There was discussion about something like `BigInt.parse(string)`, and alignment between it and `BigInt(string)`. +* There was also discussion about exponential bigint literals like `1e6n`, and some discomfort about supporting the parsing without such literals (which would incentivize dynamic patterns over static ones). + +### Conclusion + +* The PR was converted into proposal-bigint-from-exponential, which reached Stage 1. +* String parsing and bigint literal syntax expansion are both in scope for consideration. + +## Needs-consensus PR: require hosts that provide a custom global object to allow initializing ECMA-262 built-ins on it + +Presenter: Nicolò Ribaudo (NRO) + +* [PR](https://github.com/tc39/ecma262/pull/3728) +* No slides + +NRO: Okay. Yeah. I have no slides. The only thing I have is this description here. So a bit of context. This is a layering request to clean up some interfacing between HTML and 262 in some place where now it's very messy. There is one unintended normative change to this request. We talked about it among editors. We believe it's like perfectly fine. But we need to ask for consensus for it. And specifically the normative change is that, so right now the global object of a given Realm is created by the host and not by 262. This request does not change that. And after the host creates this global object, ecma262 defines its own built-ins on it. So for example it defines the Array global or the Map global and all of those. Right now the global object provided by the host is allowed to throw on that definition. And that will just then make the Realm creation throw. But with this change it will be invalid for the host to provide a global object that rejects the 262 built-ins. + +NRO: So this does not impact the web. It does not impact any implementation we know of. In practice, a hypothetical implementation that would be impacted is an implementation that allows users to create Realms with arbitrary objects. So they could for example provide a Proxy as a global object or like a frozen object that does not have these built-ins and so will throw on definition. Given this is mostly a theoretical thing we expect it to be non-controversial. But does anybody have concerns with this? + +CDA: KG is on the queue with support. + +NRO: Perfect. I see no concerns. So I assume we have consensus for this change. + +CDA: Yep. Seeing nothing else. + +NRO: Okay. Thank you all. + +### Conclusion + +* Approval for pr 3728 + +## Machine-readable source of truth for proposal info, delegate info, and maybe more + +Presenter: Michael Ficarra (MF) + +* [repo](https://github.com/michaelficarra/tc39-data) +* [demo](https://michaelficarra.github.io/tc39-data/) +* [slides](https://docs.google.com/presentation/d/1aP1LDkspsVlvjYRDN63bTIjzP6rz1m6eHbZooGNu9QA) + +MF: This topic is about the source of truth for our various pieces of data. So I have noticed some problems. I am not the only one who wants to build all kinds of tooling on top of our data. We talked about earlier DJM wanted to build some tooling for internal use at JetBrains for tracking their positions on proposals and also for sharing information internally about discussions at recent TC39 meetings. We've also heard from researchers previously about wanting to do different kinds of analyses on our data like with how our proposals advance and like what kinds of proposals they are. And that kind of stuff. Right now our canonical data sources are really primitive. They're just like kind of unstructured data in Markdown files and text files and stuff. And all dispersed over like many different places. There's also a lot of missing data. So I recently went through and added all of the missing meeting notes to proposals. I have an open PR right now. It should be merged soon hopefully. But it adds 600 meeting notes links to the proposal README. Which if you look at the proposals README afterwards it's not the best interface for that kind of data. And I also want to track more kinds of data that we don't currently track. We've heard proposals from like MBH previously about wanting to tag proposals with different kinds of topics. That kind of stuff. We don't have that right now. And that would just bloat our README files even more. So yeah. This is what the current state-of-the-art looks like. We have a bunch of different READMEs. The proposals are kind of split across different files. We have these lists of meeting note links. Which are just linking to the year and month of the meeting that they were presented at—and are at the moment really incomplete. So I want to do something like this. I want to have a JSON file that describes all these kinds of things. The same kind of thing but available in a structured format. And we could do cool things like this. + +MF: So I have a [demo](https://michaelficarra.github.io/tc39-data/) of just—I AI-generated a little static web page from this data. So you have a repo that has this JSON file in it. And every time I push to it, it just automatically builds a GitHub Pages page and publishes this web interface for searching and filtering the different proposals. And now you can see like AsyncContext has the full list of presentations there. And it's much more reasonable. And you could do anything you want with it. Really, people can build these kinds of tools. We can obviously have a canonical proposal list web page that is in the GitHub Pages for the proposals repo or something. But I think this would be a much improved experience as well as now having access to structured data so people can do whatever they want with our data. There we go. + +MF: Similarly delegates.txt I think is an awful way to be managing this stuff. It has worked decently enough for like when I'm note-taking. I can Control+F somebody's name and then find their abbreviation. But it doesn't have their GitHub handle in it for instance. So I have to go to the TC39 organization on GitHub and search for their name and hope that they have their name listed on the GitHub profile and I can find from that the GitHub handle and I can also find their affiliation. It's just really really clunky. So we can do the same thing. I can just make a JSON file and we can have delegates. It will use the structure to enforce the fact that we can't have duplicate delegate abbreviations and we can have all sorts of tooling on this. And we can do something similar. This is also on the demo page if you follow the demo link in the agenda. Then we can do the same thing just like searching for delegates. We can filter by active. We can get… you know it's all in one place now right. + +MF: And again this is just an example. I'm not just trying to get to this point. I'm just trying to make the data available so people can build more tools like this. You know better things than this. And we have other sources of data too. These are just the first two. I also started working on making the TC39 process available. We have our normative conventions, that would be nice. We have all this information in how-we-work. The agenda documents themselves are also structured data that's in an unstructured format that lots of tools process and operate on. TCQ that you're using right now parses these agenda tables. We just merged a pull request to do agenda ordering enforcement in CI, and that's re-parsing this unstructured data and it has to account for all these edge cases with people doing things slightly differently from each other. So it would be better as structured data somehow. And the structured data doesn't mean manually editing JSON files or anything like that. For all of these things you can also have graphical interfaces to them, so that it's a better experience all around. Similarly we have upcoming meeting dates and locations like that could be pulled in for calendars or for whatever. I also think that technically—I'm presenting this to committee hoping that we get sign-off to go ahead and do this. I think technically this is within secretarial discretion. I think JHD could just go do this if he wanted to. But I think it's the right thing to do to run it by everyone. + +MF: So yeah. Go ahead and take a look at this repo I have. That's where the demo page is being generated from. That's an example of where I want to get to. I'm not looking to make these—they are just the example right. I'm just trying to say like we should have structured access to our data. Or a structured representation of our data. However, how we do that is—I'm totally open to it. + +MF: I wanted to acknowledge that we also had a previous somewhat-along-this-line effort called dataset. This was kind of not exactly what I was trying to do. What I'm trying to do is make the official canonical source this repo. The dataset repo was the other way around. It was trying to parse notes documents and stuff and make them available structurally but not be the canonical source. Which is not the thing I'm trying to do. And I've talked to them—so this was maintained by JWS and I got a message today in support of my new effort. + +MF: I also wanted to show that W3C has this kind of thing. They use these REST APIs for accessing everything about their organization. So you can look up people and their affiliations and if you scroll through this web page you can… they have tons and tons of data that's available through this API which is really useful. I think we should kind of follow that example. And that's it. I wanted to open it up to discussion. + +LGH: Yes. So even though MF and I talked to each other at least once a week on the editor call I was not aware he's building this. And I built the exact same thing by accident a few weeks ago (). I sort of got it in a half working state, put it online and then moved on and never really told anyone about it. So this is not to blame or anything. It's just a happy little accident. But yeah. Definitely a big fan of this. My initial motivation was sort of wanting a visualization when proposals progress and sort of when they get tests and how big the gap is between stage 2.7 and tests and stage 3 and all of that. And I sort of turned it into a generic thing. Over time. So I would definitely be interested in unifying this. Obviously I don't want to maintain my own thing. If there's a bigger project that other people could be involved in as well. So yeah. Feel free to have a look if there's anything—mine is more like a timeline. And I also have when proposals split from each other, when they merge into each other. I do find all of that very interesting. So yeah. Definitely open to sort of providing my dataset, merging it into this and then having a collaborative thing. That's all. + +DLM: So yeah. This looks great. I maintain some internal tooling using existing datasets which are functional for that. But as you pointed out there are things that are missing. So I think yeah. This is good and would be an improvement. Thank you. + +OFR (on queue): ❤️ (also would tie into internal tooling) /eom + +SHS: is on the queue with plus one. I never know how to find affiliation and delegates list. Would love to have an easier way. + +LVU: +1. Sorely needed. For proposals I hope we can keep metadata in the proposals repo and have an automated process that aggregates rather than having proposal data scattered across multiple places. Perhaps that's already the case. I missed that part. + +EAO: Having a way to see a list of proposals I'm championing is great. + +CDA: So yes. I do like the idea of having these datasets particularly if they are regularly updated. I had also mentioned to MF earlier that I think it'd be great to be able to track delegate history. So for example many delegates have represented different Ecma members at different times. Some have been invited experts at different times. That's useful information at least historically. I'll note that we do have you know I do have code that exports we the committee basically considers the source of truth for delegates and invited experts, the GitHub team membership because that's the thing that we curate the closest. And so we have an export that spits out all of the delegates and the IEs and their affiliations and we send this to Ecma on occasion to make sure that our data is as consistent as possible. So perhaps that could be leveraged. But again that only includes the current state of things. That doesn't really help with historical things unless people are sitting in the emeritus team of a former ECMA member. + +CDA: It's a good segue into my next topic which is you know I'm concerned about what's going to be the source of truth and issues with data consistency and things being updated one place and not another place. You know we're not going to solve for that here. That's just something we'll have to wrestle with. And then finally I did mention this to MF. I have some concerns about the delegate list with their affiliation and GitHub handle and everything being extremely public. MF correctly pointed out that the information is basically public already. (omitted). But (omitted) is a different thing than having a very public delegate directory searchable and filterable etc. So I really like that. I echo some of the comments made that it'd be great to be able to find people's affiliation more easily because I also wrestle with that at various times. Every meeting and between meetings. So I would really like that. I would just hope that we could find a way to make that not completely public to the entire internet. So I don't know. We could talk about ways to do that or if folks how folks feel about that. But I am slightly concerned. + +CLA: Support. + +DJM: Plus one. Thank you so much for starting this activity. + +LGH: So a while ago someone made something similar for WHATWG (). Which has the big flaw that it attributes all the commits someone made to where they currently work. Which I am strongly not in favor of. So we should definitely avoid—like, if we do want attribution to the current delegate affiliation it needs to have past history. That's all. + +MF: Yeah. I think I agree with—I think CDA was the one who mentioned this: having the delegate affiliation history rather than a single affiliation would be a great improvement to the data schema here. + +CDA: Great. That is it for the queue. + +MF: Okay. Well I mean that sounded like basically entirely positive feedback. I'm really glad to hear that. I will work with JHD to first start with the proposals data. Get that available as structured data. I think one thing I didn't—I do want to also—like, I think you can see here on this repo, I want to publish it as a library that you can depend on. So something like publishing to npm as `@tc39/data`. Something like that. We will include all of our different datasets. If you have opinions on that let me know. Yeah. So I'll work on getting our proposals list READMEs. I'll convert it to a structured form and have a great UI replacement for that before we do so. And then I'll move on from there. If you have any opinions on what to tackle next, like the delegates list or anything else, feel free to get involved. Message me on Matrix I guess. + +MF: And for CDA's concern about privacy I will start a discussion on the Reflector—I guess is the best way to handle that. And I think yeah because of CDA's point about the relative availability of this data even though it is public I'll probably scrub the mention of (omitted) from the notes and that way we're not exacerbating the issue that CDA is concerned about. Is that cool? + +CDA: Yeah. Sounds good to me. + +MF: Okay. + +CDA: Yeah. A quick note to expand on my concern about that being searchable and public is at IBM we used to have that. We used to have a completely public employee directory and that was a problem for reasons that you can probably imagine. So I've kind of got a little bit of you know PTSD from that I guess. All right. Nothing else on the queue. + +### Speaker's Summary of Key Points + +* centralised, structured data is better than disorganised, unstructured data, so we should convert our data and make it more available +* we can have better ways to navigate the data +* people can build tools + +### Conclusion + +* MF will work with JHD to start making our datasets available as structured data +* will start with proposals repo, including a UI +* MF will meet with chairs to privately discuss privacy concerns related to delegate info + +## Needs-consensus PR: make `Promise.try` use PromiseResolve in non-error case + +Presenter: Kevin Gibbons (KG) + +* [PR](https://github.com/tc39/ecma262/pull/3883) +* [slides](https://docs.google.com/presentation/d/1x7FdF7RdGBCI6TVHrbtBMAw5tvXI2mMDthqcd5n8rc4/edit?slide=id.g106f4536d9_0_109#slide=id.g106f4536d9_0_109) + +KG: Hello everyone. I am here with a proposal to change a proposal that landed not too long ago. Which is `Promise.try`. Which has an interesting history because it was something that existed in user land libraries for a long time. Before shipping on the web about a year ago. But the user land libraries are still using their user land thing. So changes that I am proposing here would of course only affect the native one. And I'm hoping we can get away with this normative tweak to something which has technically shipped. There's a pull request and tests. + +KG: Okay. So what is my proposed change? Well if we look at the MDN description of `Promise.try` it suggests that you are using it for cases when you want to call a possibly synchronous possibly asynchronous function and ensure that you get a `Promise` out. The natural way to do this would be `Promise.resolve` of the function. But if it's synchronously throws that doesn't catch the error. So `Promise.try` according to this description and also my own intuition is basically supposed to be for when you want to write `Promise.resolve` of a function but also want to catch synchronous errors. So something like this. Is what I imagine when I read that description or think about what this function is for. But it's not actually that. It is something else. This is subtly different. Mostly in that the version that we actually have will wrap any return value. You may recall that `Promise.resolve` has this fast path special case for when the argument to `Promise.resolve` is already a native Promise. Or to be more precise it is when it is a native Promise whose dot constructor property is the built-in `Promise`. And that also works for subclasses of Promise if you have a subclass of Promise, resolve will check the dot constructor and that ensures it is a subclass of Promise. + +KG: Anyway basically `Promise.resolve` isn't supposed to wrap things which are already Promises. It lifts anything that's not a Promise. And just acts as the identity function on Promises. So this difference is only relevant on the happy path. The case where you don't get a synchronous error and you are returning a Promise from your function. So basically in the case when you're calling an asynchronous function and I think if this is that the path where you expect the least change from `Promise.try` because the whole point of calling `Promise.try` is when you want to handle someone giving you a potentially synchronous function in a place that you accept asynchronous functions. So more concretely this snippet of code I am proposing to change so that the second line would become true. If you give it something which is already a native Promise or sorry you give it a function which returns something which is already a native Promise then it just gives you that Promise back. + +KG: So again this is just it better matches my intuition. The other reason and the reason I was actually motivated to bring this is that it saves two micro task ticks. The Promise constructor giving you a new Promise means that excuse me means that you have another entry in your chain of Promise then functions when you're trying to use the result. So this came up for me in the context of async iterator helpers where I was calling lots of potentially asynchronous potentially synchronous functions and I wanted to do this lifting but I noticed if I was using the native `Promise.try` my ordering was getting messed up because I was adding these additional micro task ticks. There is a proposal where doing this Promise wrapping might someday only add one tick but it will always add at least one tick. And for cases like the one that I was using that makes it unsuitable. And even for user land like microtask tick isn't totally free. So it's nice to be able to get rid of those. But the primary motivation that I want to present is just that it better matches intuition. + +KG: Okay. So this is changing something that already shipped. Is it web compat? It is a little bit hard to tell because almost all uses of `Promise.try` are using user land stuff. I was unable to find any native uses but that's just because it is swamped by the ones that were using user land libraries that look exactly the same as the native one. That said you have to be doing something pretty weird for this to break you. You have to be depending on the exact microtask ordering or the identity of the returned Promise. And both of those are quite unusual things to do when working with Promises. Microtask ordering is also like somewhat inconsistent across engines in some functions anyway. So it's unusual to depend on that. Sorry, not to say it varies for `Promise.try`, just that there are other functions in other parts of the web platform where the microtask ordering will change. So people usually don't depend on the exact microtask ordering. That's a weird thing to depend on. + +KG: Anyway here's the proposed change. It's pretty simple. It just says instead of always doing `new Promise()` you do `new Promise()` in the error case and `Promise.resolve` in the non-error case. Here's the full spec text rendered. Like I said, there are tests in test262. No one's reviewed them. But yeah I'd like to ask for consensus for this change. Let's see the queue. + +WH: I'm curious what the rationale was for picking the current behavior. + +KG: So JHD was the champion and can maybe speak if he has a better answer but my understanding is that we were just copying the way it was implemented in userland functions and userland functions implemented this before `Promise.resolve` existed with its fast path. I did ask at the time I was bringing this up originally and JHD said that when he was championing this proposal he had not thought about doing it this way. + +CDA: Just noting that JHD is not currently in the meeting. + +KG: Yeah okay. + +CDA: We will not have the benefit of his perspective beyond what you've provided. There is nothing else on the queue right now. + +KG: Okay. Does anyone have opinions about this? + +CDA: There's a plus one from MF. Support. + +CM: Plus one, feels like a straightforward hygiene improvement + +ACE: Plus one, 'down with the ticks'. + +CDA: You have support also from DLM and WH. Sounds like you have consensus. + +KG: Okay. All right. Thanks very much. I'll take that as consensus and I'll try to get I guess the order here is I will get try to get the Test262 tests merged which will then flow into implementation issues. And for normative changes like this I don't know what the current editor group stance is but when I was an editor we tried to not land normative changes even these PRs until there were shipping implementations. So probably what will happen is I will land the Test262 tests and implementations can then fix to match the proposed spec text and then we will land the pull request someday in the distant future. + +CDA Great. Thank you. + +### Speaker's Summary of Key Points + +`Promise.try` always wraps its result in a new Promise, even when the callback already returned a Promise. This makes it unlike the `Promise.resolve(func())` intuition in the identity of the returned Promise and in additional microtask ticks. As far as I can tell this is just because we copied userland implementations which predate the `Promise.resolve` behavior. I suspect it would be web compatible to change. + +### Conclusion + +* Committee supports this change. + +## Await Dictionary for Stage 3 + +Presenter: Ashley Claymore (ACE) + +* [proposal](https://github.com/tc39/proposal-await-dictionary) +* [slides](https://docs.google.com/presentation/d/1pH5SCZRJaN-HFeOn0WZ9KD64vvunPZdxhyYuMMHZLC8) + +ACE: My name is ACE. I'm a delegate from Bloomberg. So the proposal, Await Dictionary. I'm hoping this proposal isn't a big surprise to anyone that's been around for a while. It went to stage 1 back in 2023. But for people that are new to it, here is just a summary. So we're currently in stage 2.7. We got to stage 2.7 in November last year. The problem is centered around code where they're creating an object, they've got multiple, async things. This is bad because we're doing a waterfall. We're not going to start getting that really expensive color or mass until the previous ones are finished. You— people then typically go, "Okay, I'll— I'll avoid the waterfall. We've `Promise.all`. `Promise.all` is not great as a, pass in more and more arguments." Because you now might mix up the order. So here, we're accidentally assigning color to shape and shape to color. So people then might try and extract things out. They'll first just go await all the Promises, in parallel, and then await them. This is also bad, because of meant one— I don't know. I don't love the code, but on a more technical reason, if, if shape request throws— or rejects, sorry— then like this will throw, and then maybe you've got a higher level like this. You're probably— well, you're in an async function if you're using await. So that Promise will reject, and it'll bubble up, and you might have like a try catch somewhere. But, this Promise is just kind of left angling, and so is this mass request one. Like, no, handlers have been attached to them yet. So if these reject, they'll be treated as an unhandled Promise rejection. So what we do in this proposal is we give you a named version of `Promise.all`. So we say `Promise.allKeyed`. You pass in a kind of your dictionary of Promises, and then you get back out an object. So you can destructure with the names. So now we can't mess up the order. And also, handlers are all being attached equally. So if you won't have a non-handled Promise rejection. Unless the outer Promise you're creating, is unhandled. So the name comes from matching with the already advanced, `iterator.zipKeyed`. So that's where we have the keyed name from, because it's basically the exact same thing, but for Promises. And then we also include the same thing for `allSettled`. You have `allSettledKeyed`. + +ACE: So we have spec text already, because we're stage 2.7. The only changes since going to stage 2.7 is LGH has very graciously been sending us PRs as to keep us up to date with editorial conventions and also just, some tidy-ups, but no actual, normative changes. The big change is we now have tests merged thanks to, a colleague of mine at Bloomberg, [Daniel Asaria](https://github.com/danialasaria). He has kindly worked over a series of PRs and got these merged. So we've got tests for allKeyed and allSettledKeyed. Then we have, 44 or 45 tests for both. And we're already at 100% test passing for both Boa and SpiderMonkey, which is really exciting to see. And I think the test coverage is really good. I won't try to say that it's impossible to write an implementation that is wrong, yet the tests don't catch it, but I think it would be very, very hard. We've really tried to think about, not just the typical boilerplate tests, but also about the bits of the proposal that are more interesting and you might easily miss if you adjust trying to implement the spec based on what you think it might be, or you're trying to optimize something out. The main complexities with the spec are around the fact that, like other Promise thenable, they are designed such that they handle thenables. And so there's nothing stopping someone creating their own version of a thenable that doesn't behave like a Promise, it calls the resolve function multiple times. Or it calls it synchronously. And the spec is written in a way so that we define exactly what should happen. In those cases, and the tests do that as well. If there's also complexity around the objects you might pass in, if it's a Proxy, there's a whole bunch of weird things that might happen when we're, getting the keys and checking if they're enumerable. So we have cases that handle— you know, if get own keys throws or get property description throws or something like that. + +ACE: So yeah, asking for stage 3. + +USA: Great. Let's look at the queue. You have two upcoming topics. First, we have JHD. + +JHD: I was going to say I support stage 3, and I've already built polyfills that pass the test 262 tests, and, yeah. + +ACE: Excellent. It's great to hear, JHD. + +USA: Great. Thanks. Next is LGH + +LGH: Support for stage 3. + +LVU: Support stage 3 as well + +DLM: I'm just going to say support for stage 3 as well. + +DRO: I support this one too + +USA: As well as CDA. Who also says, I support this. Oh, and he's spamming, I support this now. + +CDA: That's my subtle, reminder to people that to the right of the buttons, there's a little dropdown there where you have a default canned support message that you can select if that's what you were typing. And you can also edit it and add other saved topics or, edit the support response to whatever you would like. + +ACE: Thanks for the support, everyone. It's really exciting. I'll, do a summary and conclusion for the notes. Async. + +### Speaker's Summary of Key Points + +* A reminder of the motivation for the proposal was given +* Spec changes since November have been purely editorial +* Since November we now have 89 tests merged to test262 +* Boa and SpiderMonkey are passing 100% of tests +* There are also polyfills that pass 100% of the tests too + +### Conclusion + +* Consensus to advance to Stage 3 + +## Import Defer Stage 3 updates + +Presenter: Caio Lima (CLA), Nicolò Ribaudo (NRO) + +* [proposal](https://tc39.es/proposal-defer-import-eval/) +* [slides](https://docs.google.com/presentation/d/1pH5SCZRJaN-HFeOn0WZ9KD64vvunPZdxhyYuMMHZLC8) + +CLA: Okay. Let me move on. See, if I— if I start to break now, please interrupt me. So yeah, hi everyone. For some of you that don't know me, I'm Caio Lima. And I'm part of the champion group of Importifer, together with Nicolo. And, I mean, I think that is more people in the champion group as well, but the presentation today is— I'm representing mostly me and Nicolo here, and as a delegate of Igalia. And this work is a working partnership with Bloomberg as well. So the presentation is about Importifer, stage 3. Updates. Let's just start, with this part of the status of implementations that we have so far. So the good news is that Importifer is being implemented in advance in the implementation. In at least all the major browsers, VMs so far. So for v8, the status that we have is that it's implemented behind a flag, behind a runtime flag. It's now being fuzzed. So as soon as we get, a period of stability, not getting security issues or issues coming up in bugs and etc., at some point, the team is going to feel more comfortable to unflag and ship. And this is the same for Chrome. I mean, the whole process is actually shared between Chrome and part of v8. But if we look to Deno, Deno already is shipping a version that is enabling Importifer by default. And that is work being done on Node.js to import, mainly the part of dynamic imports. For Importifer as well, from people from coworkers that we have here at Igalia. And the JavaScript core situation is that it's also implemented behind a flag. I saw that the implementation includes some integration already with Web Core. But I'm not sure— and I don't have, any information that the status of Safari, if it's possible to enable-by-default in any tech preview or anything like that. But the thing is that Bun is already implementing— or is already enabling the feature by default. And that is work, already work in progress for SpiderMonkey as well. Okay, so for the last, I think, two months, since we last talked about Importifer, there were three new issues open into the repository. And one of them is actually a bug found by the fuzzing from v8. That is a bug in this spec. And also in the implementation. And the other two are actually topics that opening discussions about the design of some parts of the proposal as well. + +CLA: I will start with the bug because I think it's one of the most critical ones. But the other two, they are also important as well. So this is the issue number 85. The title of the issue is properly check if a strong connected component state when looking for a syncing appendices. I think the title is going to make a little bit more sense whenever I give the example here. But just before giving the example, let me give you some context. Whenever we try to check if the status of a module is evaluated or not, throughout the spec right now, we actually do that looking up for the cycle root instead of the module itself. And the thing is that, the way we have the specification right now for Importifer, main two points. So one of the obstacle operations called gather asynchronous transitive dependencies in order ready for a sync execution. Both of them actually check the status using the module itself and not the cycle root. And this is actually problematic. So let me show you the example where this comes up to become more clear what's going on. So imagine that we have this cycle here. It's a very simple cycle. We have A that does— that has a dependency on B. And it has top level await and also B that has a dependency on A. So whenever we actually do import of A, what is going to happen throughout the execution of the algorithm here for the module is that, both of the module is going to move their status to evaluating. But since A depends on B, and the way the entry point A is here, whenever we look up for the dependencies of A, we arrive in B. And then, B goes back and cycles back to A. And then, what happens is that, since B doesn't depend on anything else, and can't evaluate already, they move its evaluation and finish its evaluation. Moving to evaluated. But how— since A has top level await, what happens here is actually whenever the statement of await is reached while A is being evaluated, it's going to move the status of the evaluation to evaluating, I think. So we will end up with— after, the import A, we will get, the output of import A is going to be a Promise. That is going to settle whenever, we finish the evaluations of A. But the graph is going to end up into this situation. So before A be fulfilled, if we have a situation where we do a dynamic import of C, and the C has an Import defer on D, and D does— has a dependency on B here, so we will have, the situation where actually C and D, they kind of transitively depend on A as well. But what happens is, at the beginning of the evaluation of C, by the spec and the semantics of Importifer, we first need to get all the asynchronous transitive dependencies of C, because the design of the proposal that we intend right now is that, make C evaluate— wait for all the asynchronous dependencies of C finish its evaluation. So we can, synchronously evaluate C in this point here. So the way we do that is through this abstract operation. And the expectation from this graph here is that whenever we call gather a synchronous transitive dependency, we should actually get a list with A. However, if we look to the spec so far, I'm not going to step by step here, but basically, like, whenever we recurse at this point, we see that on step 6, we check for the `module.status`, if it's either evaluating or evaluated, we return an empty list. We create the empty list in the step 2, and we return this as an empty list. So if we go through, what happens is that, we gather asynchronous dependencies of C, that we will actually call recursively to D. And then we eventually reach B. And B is the status evaluated. So we never actually have the chance to go through A and check the status of A here. So what is going to happen is basically, it's going to return an empty list. And skip A. So given this happens, C starts its evaluation and finishes evaluation. But what happens is that if eventually we call n as full here, and n as he presents namespace object, that has a deferred module for D. So the expectation here is that the evaluation of D should be triggered. And at this point, we also do a check to see if it's able to evaluate D asynchronous in this case. So ReadyForSyncExecution, this is a very similar algorithm from gather asynchronous transitive dependencies as well. And they also have the same idea of, recursing through the modules, looking up to the status of the module, and we do have this short circuit here whenever the status is evaluated, we return true. So what happens is, again, we call ReadyForSyncExecution transitive— ReadyForSyncExecution of D, that we'll call ReadyForSyncExecution of B. You will see, evaluated and, short-circuiting never, seeing the status of A. So this is going to return true. But the problem is that this will make actually `D.evaluate` return a Promise that is actually pending on A. And because of course, A is still not settled yet. And that actually breaks an assertion that we have in the spec right now. So what happens is that, whenever we are able to call `module.evaluating` to a module that was deferred, it should actually execute synchronously. So the state that we are allowed for the Promises here is either fulfilled or rejected. And yeah, so that's essentially the bug. + +CLA: So the fix that we are proposing here is actually to change the spec to instead of look up into the status of the module itself, check for the cycle root of the module. So the idea is to introduce this— the proposal is to introduce this new abstract operation called `isModulelSccEvaluated`. So basically checking if the whole component, the strongly connected component, is evaluated. And if it's evaluated, we return true. Otherwise, we return false. And there is a little bit of boilerplate here because there are some situations where the cycle root might be empty. But the thing is that, if there is a cycle root into another module, we get the status of the cycle root. Otherwise, we fall back to the status of the module itself. And of course, go over, asynchronous transitive dependencies and also ready for sync execution and change either step 6 here to instead of look up to the module, we use this new abstract operation. And, also instead of looking up into the module itself, we look to the cycle root instead. And use the abstract operation as well. So with this change, so the behavior that we will have in this case is like that. So whenever we call import C again, gather asynchronous transitive dependencies is going to return A. With essentially make C not start its evaluation, then what's going to happen is, C is going to wait until A is fulfilled. It's going to register itself as a dependency of an asynchronous dependency of A. And whenever, A finishes evaluation, C is going to start its evaluation of the awards and, instead of moving to evaluate, it's going to move it to status to evaluate this thing as well. And at this point here, whenever we call `ns.full`, we will be able also to return for ready for sync execution of D. We will be able to return false as well. + +CLA: So I think that's the part that I— before moving forward, I would like to check how the queue is. And ask for consensus, like, on the change that we are proposing so far. + +USA: The queue is empty at the moment. + +CLA: Yeah, I cannot see the queue for now. But let's wait a little bit to see if anyone has any concern. Should we consider that I have a consensus, though, for this then? + +JHD: Yeah, sorry. Consensus for what? This was an update section. Oh, yeah. So this is the bug. And that is a fix being. I'm also not seeing a presentation. Are you supposed to be showing something? + +CDA: There is a presentation. + +JHD: Oh. It's not showing on my end. Okay. Maybe it's just me. + +CLA: So the consensus that I'm asking here is, to the fix for the bug that we found. So do we have a consensus? + +CDA: There are items on the queue. USA, are you there? + +USA: Yeah. Sure. First, we have KM. + +KM: I'm for fixing this. I just kind of wonder if there's, a way we can— it seems like we've had these kinds of issues multiple times in the past, if I remember correctly. And I wonder if there's a way we can do a better job at avoiding these types of things. I mean, I worry— like, this particular one isn't likely to be a security bug or anything— but in any engine or anything. But it would be nice to have more assurance that our module loading semantics don't create a bunch of issues. I mean, I think you have other subsequent ones you're getting into. Is it, I don't know, would it be— is it even remotely possible to have some kind of, machine verified proof of, the characteristics that we want of our module loader in general? Just because I don't think anybody has in their brain the entire functioning or maybe it's not even capable of maintaining all of the things / invariants that we care about in the module loader. Not necessarily an action item for right now, but just a thing I thought about. + +CLA: Yeah. I think maybe NRO has more details than that, but I know that that is, a project right now trying to do, validation—a proof validation—mechanical proof validation of the module systems. But so far, they are still in the leaking part— linking part instead of, the evaluation part yet. + +NRO: If I can give some details there, MBH and I are working together with a PhD student of his to formalize this part of the spec. As you'll know, it's unfortunately a very complex part of the spec. So it is taking a while. We do not know how— what type of results we'll get from there. But hopefully something useful for the committee. + +CLA: Yeah. Another thing that actually is useful— I don't know, how JSC does in this case, KM. But, V8 implemented— I think two months ago, fuzzing support for modules as well. And, like, the module— the whole module systems is being fuzzed right now. I think we found this bug from motherboard fuzzing parts. But I've been seeing, other issues that were more related with v8 itself. But at some point, looks like if you see the coverage for test 62 and potentially the coverage of all the implementations, we don't have, a lot of conversion ESM in general, you know? So maybe this fuzzing support on Fuzzilli actually will help also have more conversion in general. + +KM: That makes sense. I mean, I guess if we do that, it would be good to make sure we're very thorough about putting— I mean, I haven't been on the implementation layer also, but in the spec, all the invariants that we actually expect the module loader to have. And implementations can add the same assertions. And then fuzzing will hopefully bind to those assertions if they are wrong. Or, I guess, in theory, AI modules can try to construct ones that hit those assertions. + +CLA: So yeah, that is really a reply on the queue from Kevin regarding fuzzing, right? + +KG: Yeah. Yeah, I was just going to say, formal verification, it's great. Fuzzing also great. And a little bit independent. And you don't necessarily need a real implementation. It seems like the sort of thing where you could write down, a small part of the module loader and fuzz that more directly. Which might be an easier task than formal verification. Especially with, yeah, just because formal verification requires you to do all of this theory building as opposed to just sort of blind implementation. + +KM: Yeah, totally. I agree. And I think Kevin— like, that's where having the authors of these proposals put their assertions— their invariants— assertions. + +KG: Strongly agree. Invariants in the spec, A+. There are a good number already. + +CLA: So we're moving on the queue, I think. It's OFR? + +OFR: I just wanted to plus one this that I think the module system is like a part of the spec that is very easy, it seems, to get wrong. It's very easy to invalidate invariants and that could use more scrutiny. I think to the question before, whether this was now a security issue or not, I mean, we don't track it because it's not shipped yet. I think it's safe to say that many of these issues could be turned into a— into some sort of a security problem. So this is quite important. And when it comes to fuzzing, I think there is an extra complication here that is that mostly— like, a big part of the module loading system is kind of at this boundary between HTTP fetch, render, and JavaScript engine, which makes it really hard to fuzz. So yeah, it's also not a great tool to rely on in the case of modules. + +KG: Well, that makes it very hard to fuzz a real implementation. But when you are just concerned about correctness of the spec, you could just have a toy implementation just of the module machinery. You don't even need to do that in an engine. In order to do this sort of thing. + +OFR: Yeah, that will give you some of the bugs, I guess. Yes. Like, that will give you the spec bugs. Yeah. True. + +KM: It's also probably possible to have— when you have a lot of these invariants in here, have passed to an AI model— asked an AI model to try to construct counterexamples that would cause problems or break them. It's generally decent at that. Even if you don't actually have code. + +CLA: Yeah. Maybe probably you need— I mean, I try to do that sometimes with Claude Code and I get blocked. Because they think I'm actually trying to find a security issue. But yeah, if you have the ability to kind of work around all those bug radios, it would be awesome as well. I mean, I was trying to find security issues, but not, to exploit those. + +USA: All right. For the queue, we have support now from DLM, JSL, now, and PKA. And that's it for the queue. Maybe let's give folks a minute. Although nobody's adding anything to the queue. CLA would you like to conclude? + +CLA: Yeah, so given, the status of the queue so far, I think I consider, the proposed solution for us to change the spec and fix the assertion is consensus. And we now need to essentially, update the implementations that are broken, though. + +USA: All right. I don't hear any dissent to that either. Would you like to— sorry, I meant— would you like to speak out a summary or a conclusion? + +CLA: Oh, yeah, sure. So the conclusion here is that— + +NRO: Sorry, slides are not there yet. Sorry, CLA, let's finish presenting. + +CLA: Oh, yeah, okay. Okay. Okay. Okay. Okay. Yeah, there are, two other issues as well. Sorry. I will stop, the queue. Also, again, and after, two times before, the conclusion. + +CLA: So the second issue that we got in the repository is the issue number 83. So the title is, should gather asynchronous dependencies stop traversal when a module is evaluated, I think. So this issue was opened by Yoshi from, I think, it's from Mozilla implementation, looks like. I don't know, if it's a Mozilla employee person anyway. But the thing is, the person is involved in implementing Polyver or SpiderMonkey in Firefox. And then, we had this issue where whenever we have this situation, when there is a cycle between a module that is importing, has a dependency of using static deferred imports, and another module has a dynamic import, we can have this situation of that block. So it's too abstract. What I'm trying to say, this situation here in this screen. So if you have a module A and A does import defer from B, and there is, a B that doesn't have any dependency, but there is a dependency from the dynamic import through A. We can have a deadlock in this situation because of the following steps. So if we start, the execution calling `import.defer` A, what is going to happen per spec right now is that if you need to gather the asynchronous transitive dependencies of A and, in this case here, we have a B as one of the dependencies, because it has top-level weight, we then, will start the evaluation of B. And we skip the evaluation of A itself. So in this case here, we have gathered asynchronous transitive dependencies of A returning B. And what's going to happen is that B is going to start this evaluation because there is no dependency at all. So whenever it reaches the status— the statement of a weight, we will basically stop the execution of A and skip— move the status to evaluate async. So in this case here, the operation that is being done by B is actually doing a dynamic import of A. So what's going to happen right now is that, A is going to start its evaluation. So what happens is that, given the import defer on top of A, before it starts its evaluation, we gather the asynchronous dependencies of A in this case. Sorry, the slide is wrong here. It should be gathered asynchronous transitive dependencies of A. And then, actually, of B itself. Sorry, it's correct. And then, it's going to return B. So what's going to happen here is that A is going to wait for the evaluation of B, register itself as asynchronous dependency of B, and not start its evaluation. But we have the situation where, B is waiting for A and A is waiting for B. So this is essentially, a deadlock. + +CLA: So what's actually suggested into the issue is that whenever we have this situation, we should actually allow A, for example, starts its evaluation and not wait for B in this case. So basically, means that whenever, gather asynchronous transitive dependencies finds a module that is in the status of evaluated, I think, we just, keep, like, the stop the traversal and keep, processing the other dependencies. So, yeah, the thing is that this suggestion comes from a WPT test that actually is verifying this behavior itself. Oh, yeah, there's another thing that is important here. So in this case of A here, whenever we have an access for the namespace, what's going to happen is that, this access is going to throw an exception. Because since NS is not ready to be— since the deferred module of NS, which is B, is not able to evaluate synchronously here, we're going to throw an exception, because it's not ready for sync execution. So the WPT test was testing exactly this behavior. And this was actually mostly overlooked. So I was one of the persons who implemented the test. And it was an overlook on the spec. And it was not, by intention in the end. So we discussed, this meeting in the champion group here and the thing that we— the two points that we want to bring here is that, it's possible to have the situation if you just use eager imports. So it's possible to deadlock a very similar case here if you don't use import defer at all. And the whole thing is that if we apply the suggestion on the issue, what is going to happen is that some valid case is going to stop working. And this design principle that we have for import defer where we would like to make the execution of a module that has import defer tries to execute asynchronously as possible, of course, depending on the graph that you have. And whenever you have a case, for example, where you have an import defer of a module that was started its evaluation because of another dependency outside the cycle, and it's still, evaluated, I think, but eventually it will settle, we will start to throw exceptions not ready for sync execution for those cases. And, yeah, those cases could be valid cases, for example. So, yeah, given, like, those two points, the suggestion that we have here, the champion group suggests to not change the spec. And actually change the expectation of the test itself. And, yeah, based on this discussion as well. So before moving forward, I would like to go over the key to see. If you have any concerns on this direction. + +USA: Cool. There's nothing on the queue yet. Let's maybe give it a minute for ourselves. DLM is on the queue saying, "Support changing test end of message." OFR? + +OFR: Yeah, sorry. I think I missed the point. So it's the proposal to keep deadlocking, or? I'm— + +CLA: Yeah, supposed to not change the spec. So the spec is written, this way. Mostly looking for the direction of whenever we have asynchronous dependency of something that is being imported deferred, we want to eager evaluate those, right? And if we change the spec this way, there are some cases where we would be able to eager evaluate them, wait until, like, they finish to start the evaluation of the module. And if we change the spec, there are some cases where we will never going to be able to do that anymore. So the module is going to start evaluate while, the dependencies are still evaluating, I think,? Did I clarify your question? + +OFR: I guess. And NRO is also on the queue saying that the same thing happens without defer, which I guess I wasn't aware. + +NRO: It is already not difficult because of deadlock if you have a cycle mixing top-level awaited dynamic imports. In this specific case, in this specific example, it will be possible to avoid it for import defer by making some trade-offs. But, we suggest to keep it similar to how it works without defer. And so keeping the deadlock. + +OFR: Okay, got it. Thanks. + +USA: That was the entire queue. + +CLA: Okay, so can I move forward to the next topic, though? + +USA: Yeah. Given that, well, yeah, you didn't need to ask consensus or anything, right? + +CLA: Yeah, yeah, that is no consensus here. + +CLA: So the last issue is issue number 82 here. So the title of the issue is, "Should we disallow import defer of synthetic modules?" So let me give, like, a little bit of context here. So the synthetic modules are used by hosts, mostly, to implement things that will interact with the ESM. So if you take an as an example, we do have, JSON, CSS, and byte texts byte and text modules that are created on top of synthetic modules. They're kind of concrete classes of them. But also, if you see outside browser scenarios, CommonJS, for example, interacts makes, the common JS modules interact with ESM throughout the synthetic modules as well. And there are some implementations, it's not the case of the spec and also not the spec of some browsers as well. But there, some implementations that are exposed was modules using synthetic modules as well. Some point. So given that, what happens is that import defer is considered, they already work in some situations where basically, they work in everything that can be imported. So you can do import defer on any kind of module. And the thing is, the defer here, as a design principle, we kind of do try to do the best effort saying that we will defer the evaluation of the things. But not necessarily, this is going to happen all the time. So, for example, if you already do import defer into a module that has top-level await, the effect of this is actually pretty much nothing because the top-level await module is going to be eager the evaluator, so nothing is going to be deferred as well. And if you think, in another scenario, more drastically as well, if you have a module that just has declarations in general. That's going to have declarations, an import defer on those modules also going to be, quote-unquote, "useless." Because this whole declaration really doesn't evaluate to anything in the end. I mean, there are some evaluation that might happen, but the thing is that, it's not very heavy loaded to skip the evaluation of those things as well. So, yeah, given, we have this, if we think, the case of JSON modules, whenever we try to import the JSON module, it's not possible actually, it's possible to skip the evaluation, but it's evaluation is pretty simple. It's basically creates the default binding. The parsing of that JSON module needs to happen to load in time. Because we do throw syntax errors if we try to parse the JSON module eagerly in this case of situation. So we wouldn't be able to, for example, skip the parsing of a JSON module. So v8 throughout, some internal discussions, while we were working into some of the module proposals, actually raised these questions that if you're trying to do import defer in such kind of modules like JSON modules or text modules, and et cetera, it's probably, an error because you don't really mean it. It's going to be useless at some point. It's like a mistake. And if it's a mistake, it would be a good idea to try to treat this as an error. So we in the champion group, balanced this kind of things, and we created, these pros and cons of such of taking such a direction. And one of the pros that we have here is, if you use import defer in some situations that they are useless, it's better to not fight the user as soon as possible. In this case here, before the application is running on production, that it's an error and nothing is going to really happen into the application. So they are not going to get their expectations of improving the boot time of their startup time at some point. But when we look into the cons here, is this kind of reasoning of throwing an error for such cases is kind of confusing. Because if it is useless, for some of those modules, it's also useless for the top-level await. And we are not, really considering, skip and throwing in cases where we try to do import defer for a top-level await module directly. And this will also, bring some complexity to the users to understand, which kind of modules they can use defer and which kind of modules they cannot use defer as well. And it's really not possible to, I mean, the expectation of the proposal is that we would like to keep supporting common JS and, deferring the evaluation of common JS. And we cannot ban, import defer throwing in every single synthetic modules. So we would need to create, this way of some modules some synthetic modules they kind of throw eagerly when they are using import defer and others, they are allowed. And this will introduce, some overhead and some complexity, not a big complexity in the spec. But I would like to keep avoiding introducing, new complexity at this point as well. So given those two reasons and, when waiting the pros and cons here, the champion group also suggests the direction of not changing the spec. Keep you allowing import defer behind, this idea that import defer is the best effort. So it can actually pretty much have the effect of the import without defer. There is likely some difference when you do import defer than a common import. But we still think that it's still reasonable, to keep the current scenario that we have right now. I would like to go over the queue. Based on this direction that we are suggesting as well. + +USA: First one on the queue is JSL. + +JSL: Yeah, it definitely recognizes the difficulties here with synthetic modules. But I think what I would prefer is that this is an implementation choice and that the runtime would get to say, okay, for this particular module, I'm going to ignore the defer. Because I can't do it or don't want to do it. But still leave that option open. You know, part of this is, you know, some runtimes, like, workers, we have additional types of synthetic modules that we might be able to actually implement this on. We don't necessarily want to have a blanket loss. Of this. So I'd very much like to leave it up to implementation choice. + +USA: Sorry, NRO, we have a reply by OFR. + +OFR: Yeah, so the reason that this came this even came up is that it's actually not possible to ignore the import defer because the way the spec is written, you can actually observe if a module was imported deferred or not deferred. Because in the spec, the deferred and the not deferred namespace have a different identity. So if you have, for example, ambiguous module resolution, then you will get a different result depending on whether the object the module was imported deferred or not deferred. And that's the reason it came up. That said, I think from our point of view, throwing an error is not a great idea either. So I'm actually not, this was presented as came up in v8 discussion. But I don't think it's a good idea to treat deferred and not deferred differently, to be honest. But what we certainly cannot do is ignore it. + +CLA: So when you mean, ignore it, just to clarify here, are you meaning, changing anything that we have in spec right now? Or, not changing anything? + +OFR: So correct me if I'm wrong, James, but I understood your proposal that basically the engine would just do as if you would have written import without defer. And that's the thing. + +JSL: Yeah, essentially, what I'm suggesting is, import defer yes, if you can defer it, if inside the runtime you don't actually defer it, then that would be an implementation choice. + +OFR: Oh, okay. Yeah, that's totally possible. Yes. + +JSL: But the observable behavior would be the same, either way. + +CLA: Yeah. So the suggestion that I'm making here is that, whenever you do import defer, you still, like, keep creating the deferred namespace. All those things still hold and the thing is that what happens basically, if the implementation, for a synthetic model, would like to eagerly evaluate, they do. If they want to actually defer, they do as well I mean, we already have that for top-level await models, right? So if you do import defer in a model that has top-level await, it's going to start its evaluation anyway. + +USA: Is that satisfying? Should we NRO, do you want to add to this? + +NRO: Yes, I have an answer to JSL. So there is two cases here. One, where the evaluation is observable, and one, where it's not. When it's not observable, you can do whatever you want, right? Because users cannot observe it anyway. Like, for example, not JS built-in modules. Sure, you could decide to, eagerly evaluate that, even if it's important to import defer, as long as, v8 can still build the deferred namespace object. Because, well, there is no way for JavaScript to tell anyway other than maybe measuring performance of them accessing for the first time a property unit. For modules where there is a set effect, we actually talked about this very early on in the proposal with Guy, who was concerned about, I think at the time was the Fastly implementation maybe, where if you have import defer of, JavaScript module. And for some reason, you would want to make sure that the deferral does not actually happen. What you can do is just say, oh, well, actually, my implementation has an entry point that preloaded that module before everything else. So when then you import that module, that's already evaluated. It's still not evaluated, in line with the graph, as if it was without import defer. But it gives hosts a way to, like, well, ensure that the module was already there, already evaluated. It's similar to how, on the web, you might have a script tag that imports a module that has an import defer. But maybe there was before a separate script tag that pre-imported that module that was with deferred and so caused it to be already evaluated beforehand. So I don't know exactly which was the two cases you were considering, but I hope that this can help you for your implementation. + +JSL: No, I definitely think that makes sense. I just put a reply there on the queue that I think as long as it's not mandated to be a throw, I should be fine. + +USA: Next, we have JHD. Just a heads up that we have, three minutes if yeah. + +JHD: Okay. I'll try and make it quick. So, yeah, I don't see any reason why synthetic modules should be special for users. It's I from what I can tell, there's reasons why they're special for implementations. But that's those are separate things. I think that we shouldn't, we should not do this, that we should allow everything to be deferrable. And then separately, defer isn't a guarantee as you talk you just mentioned, right? Like, there's no guarantee that like, it's just it's just an indication that you don't have to load a module right away for this for the current module to work. You don't have to load a dependency right away. So it is always completely fine if you put defer on something and then it doesn't actually get deferred. That's, always an option. So it doesn't really matter if it if an engine chooses not to defer it. Like, that's the deferral is a it's like letting go when I defer a dependency, I'm letting go of it from my module. I'm not forcing it to happen later. Yeah. + +CLA: Yeah, I vouch for that as well. + +USA: OFR is on the queue saying plus one. And then we have a reply by NRO. + +NRO: So I agree. We should not throw and let users do import defer of these modules. I think probably linters should complain. Because, if I do import defer of a JSON module, like, import defer and then put with type JSON in the attributes, that is indeed, a mistake. So I'll agree that the language should not enforce it. I would hope a linter gives me a nice quick line editor telling me look, probably you're not doing what you expect to do here. + +USA: And that's it for both the time box and the queue. + +CLA: That's the end of the presentation as well. + +USA: Okay. Would you like to make any conclusion or dictate a summary? + +### Speaker's Summary of Key Points + +* All major browsers started their implementation, with JSC and V8 being available behind a flag. +* An assertion failure (issue #85) was found by V8’s fuzzer and a proposed fix on spec was presented +* Discussion on issue #83: Should GatherAsyncDepends stop traversal when a module is evaluating-async? + * The proposal from the champion group is to not change the spec. This would break other valid cases. +* Discussion on issue #82: Should we disallow import defer of synthetic modules? + * The proposal from the champion group is to not change the spec, given con points overweight pro points for doing that. + +### Conclusion + +* Regarding the issue #85, we got consensus of applying the fix that was proposed by champion group, which changes the check the status of the `[[CycleRoot]]` instead of the module itself. +* Issue number #83 and #82, we are following the champion group direction to not change the spec and change tests that are asserting the wrong behavior. + +## Thenable Curtailment for 2.7 + +Presenter: Matthew Gaudet (MAG) + +* [proposal](https://github.com/tc39/proposal-thenable-curtailment) +* [slides](https://docs.google.com/presentation/d/1kHq_UqMHGGT8ena8yMlJKgHmohDVqxJvV4hiCB12HtU/view?) + +MAG: All right. So this is an advancement request for the thenable curtailment to go to stage 2.7. To update where we're at. So if you don't recall, the goal of this proposal is to provide an abstract operation whereby code can resolve a Promise without running user code. The way it does this is it defers by one tick if and only if the value being resolved is an object where something could run user code. So the new abstract operation here is going to be called SafePromiseResolve. And the criteria that get checked is approximately the object is a Proxy. Or something on its prototype chain is a Proxy. Or something has a "then" property. Which is in turn a getter. The actual specification is up. And there's a couple of little nits on this one of which I do actually want to talk a little bit about. One thing I will say, though, before I get there, is previously a version of this had an opportunity for users to opt into this. I have removed that temporarily. I pursue that under a separate proposal. But for now, I'm focused on getting this one done. And then we can come back. + +MAG: The editorial question really is, how do you name the set of internal operations which could potentially actually cause user code to run? So during the 2.7 review, NRO points out that there's no safety against anybody creating an internal slot or an internal operation of `[[GetPrototypeOf]]` or `[[GetOwnProperty]]`, which actually could have code user code run. His original proposal was to name the set where we know it's safe, which I just don't like because it's naming a finite set which could go out of date. What I wrote down as a mitigation here in the current spec text in the repo is if an object uses different steps than the ordinary object for the get prototype of or get own property of operations return true, this basically says that once you see an object that has these different steps, we assume that maybe it could run user code. It does broaden the set of things that are classified as could run user code. And I don't love how much more broad it does. Technically, it actually goes all the way to TypedArray, which otherwise should be fine. So I'm open to further feedback on how to write this. It is kind of an editorial thing. Just that I couldn't quite get to a solution of by today. But I wanted to mention it because it would be a totally fair thing for somebody to be like, no, like, let's block until this gets decided. And I would understand on that front. The other thing I wanted to update on is that I have been having conversations about getting this adopted by WebIDL, which was my original goal for this is that safe Promise resolve should power the WebIDL Promise resolve steps. And this is what is going to ultimately make the web platform a lot safer. Is because it means that web browser developers who are writing code will no longer suffer the indignities of having a thenable come in and mess with everything while their C++ code is on the stack. So I think I've managed to convince some people in some hallway track discussions at Web Engine Hackfest that this is going to be a good idea. And that we probably will be able to make progress on it. But I kind of need to get it through TC39 first before I can start doing the WebIDL PR. + +MAG: Another thing to mention there is an existing prototype. So there's this config preference in about:config. You can go down Promise experimental safe resolve equals true. That'll then use in all of the WebIDL resolve spec steps. It will go through the safe resolve path as prototyped by me. I can't say that this means anything for web compat, but I've had this turned on by default for a month now. I have never once noticed anything break. So hopefully it will all just work when it all when it comes time. And now we can talk about the queue. Which I can't see. Sorry. + +USA: I can. No worries. First, we have NRO. + +NRO: Yeah. So actually, this is a normative question because this affects for some objects what happens when you put them in a thenable position. So regardless of what the conclusion here will be, unless we come to a conclusion now that I don't think we will because I think it's important to have spec text in front of our eyes for this. During 2.7, you should come back saying, “look, this is the conclusion there. Does everybody agree?” The second part of my topic is actually wrong. So I'm going to skip it because KG explains why it's wrong. + +USA: Great. That's cheating. KG, you're next. + +KG: Yeah. You are saying that the problem with trying to enumerate the bad cases, like, Proxy is that, someone could have their own a host could have its own exotic object that runs user code. The solution to this, it seems to me, is to just be able to ask hosts if something runs user code. Like, we can have a host. Like, just take this operation and stick the word host in front of it. And so the default implementation is to return false. And then if anyone ever writes an exotic object that this is not—this property does not hold. They can put it in there. But in practice, no one is ever going to have such an object. So everyone can continue just returning false. And we can go about our lives. But not, accidentally roll out type arrays, for example, which the second part of my comment is resolving a Promise with a type array is, a totally reasonable thing to want to do. And I think it would be a shame to exclude type arrays from this operation. + +MAG: Yeah. Making this a host hook had not occurred to me, but actually, that's not a bad idea. The kind of fits the right shape of this where the problem is that what happens when you do get an object from outside that is got internal slots that you don't really know how they work. So making this a host hook and saying, you know, ask the host, the host gets to say, yeah or nay, mostly it will say nay. I think that that's a perfectly good answer that I didn't come to by the time I'm here. + +KG: Yeah. And I would be a little more hesitant if I thought this was actually going to come up very much. But pretty sure in practice, it just doesn't. Like, I'm not aware of any host objects in any platform where the answer would not be "it doesn't run user code". + +USA: Moving on, we have JRL next. + +JRL: So a host look seems fine to me, but do we really trust that browsers are going to know what good or not good to evaluate? Because they couldn't predict that then is going to evaluate user code. Second, type array in this case, actually doesn't matter. The only thing that would be penalized if I had a typed array that had a `.then` method on it. So I make the typed array a thenable. Then it becomes penalized. And then it only becomes penalized if it's used in a WebRTL spec. It doesn't actually matter for the grand scheme of things. The security vulnerability that we're actually trying to fix here are C++ objects or host objects that are not expected to ever be thenable that then have a dot then method attached to them or their prototype we need to make sure that that then code does not run. So I don't care that typed array, if you do it with a WebIDL, you make it a thenable and then you've tried to resolve it with that thenable becomes slow. The point is that we're trying to solve a larger class of security vulnerabilities. + +KG: That's just not descriptively accurate for what the thing does currently. + +JRL: How it is being used right now in the spec is only for Promise resolve for this one special case. The fact that is it an abstract operation that says can run user code and is only being used by Promise means we should probably rename it to something that is specific to Promise denable. We just made it a generic algorithm. The thing that actually matters for this proposal, + +(transcription error) + +KG: I'm pretty sure this guards whether you can read `.then` in the first place. So it doesn't only affect objects which do have a `.then` property; it affects objects all objects which you are resolving a Promise with in a WebIDL spec, which does include TypedArrays. + +MAG: Yes. So if you— as the spec is written, as the text is written right now, where you restrict it only to the ordinary object operation set. If you were to have a WebIDL Promise, or a WebIDL project— WebIDL resolve— of a TypedArray, you would in fact get penalized right now because it would return true for property read could— I can't even remember what we called it after. It's gone through some editing recently. Has a user running— a user code running Promise property access. So yes, the— there would actually be impact, and I think would it— is it impactful enough to worry about? I don't think so, but this was some last-minute feedback that I didn't get a chance to resolve, and so I'm okay with saying let's come back and take another stab at this. It is not so impactful in the sense that, there are not a huge number of proposals— or there's not a huge number of things that return Promises that resolve with a TypedArray. So I— + +KG: You mean the fetch spec? Like— There's a `.bytes` method on there in response body. I put it there. I know it's there. + +MAG: Yeah, no, that one— that one would be impacted, yes. So I think we should find a way to not have TypedArrays be just it being a TypedArray if it is sufficient for it to have a extra tick injected. I think the host hook answer is a good one. I haven't written the text to actually do that, so I would say probably not stage 2.7 today. I need to write that text. I might try to get it done for tomorrow and see if there's a continuation slot. Because it's like a little tiny bit of text. + +JRL: I would like to respond directly to that. This is a security vulnerability. It's been present now for months that we know about. I don't think we should delay on this specific case. Also, TypedArray is not observably different. It doesn't matter that this could affect TypedArray. The only way you could ever observe it is if you actually made the TypedArray thenable. So please, let's not wait another 2 months in order to get this to 2.7 and then get this fixed into actual browsers. This is a security vulnerability. We've discussed it enough. Please let's advance this. + +USA: MAG, would you like to respond or should we move on with the queue? + +MAG: I mean, I'm happy to— the thing is, stage 2.7 is going to be long for this. Not the least of which, because I have to figure out how I'm actually going to write these tests. So I would love to get started on this and not block on this specific small test until we've gotten that. I do think I've got time to get it into a continuation if people want that path, but, you know, I would be willing to ask for advancement right now. + +USA: KG's on the queue with an offer. + +KG: Yeah, I was just going to say, I can write the spec text for you by tomorrow. That said, JRL says this is not an observable difference, so that makes it an editorial question, which we could resolve at some future time. I would be happier if we just wrote down the host hook; it's easy to do. And we can do that by tomorrow, I'm sure. But I don't think it necessarily does need to block if it's genuinely not observable. + +USA: There are replies incoming. Well, NRO. + +NRO: It is observable if you really try, if you put a `.then` accessor on TypedArray, it's just not observable in the hopefully normal way that people write code. So it is normative, but it has very minor effects. + +JRL: I mean, it's only observable if you do the thing that this proposal is explicitly trying to change. So if you make a `.then` on a TypedArray, and you make it a getter, then you can observe the fact that it's changing. But it doesn't matter that it was a TypedArray because that same behavior is being applied to any thenable getter. + +KG: Okay, yeah, I'm just— I am broadly uncomfortable with saying that, TypedArrays are not like ordinary objects for the purposes of this operation. And I think it is, pretty easy to ensure that they are treated the same, and we should do so. + +JRL: Okay. Can we go for stage 2 advancement contingent on us writing this host hook? I just don't want to delay this part of the proposal. + +KG: Sure, I guess. I don't think this is a delay in practice. + +MAG: So NRO's got a comment, and then also I wanted to get to OFR's, which I managed to finally open the TCQ. I want to see his comment. + +NRO: Oh, yeah, yeah, I was also in the queue saying I really think you should ask for stage 2.7 now, and this is something we can solve during stage 2.7. It seems like nobody is against KG's opinion. It's actually the same suggestion I gave at the beginning, to have a list and let the host interject in that list. It was just that you were worried about the complexity of maintaining that. But it seems like we have somewhat of an agreement anyway, and you should go to 2.7 and then come back for consensus during 2.7 with the actual fix. + +MAG: Yes, and I commit that we're going to get this done by the end of the week, for sure. + +USA: Next, we have— OFR? + +OFR: Oh, okay. Yeah, this may be a bit off-topic, so maybe pick others first if there's something more on topic, but I was just like, I caught on to this idea of, we— now we have a problem that the spec doesn't tell us what is executable or where can we reach user executable code, and I think this is, like, a thing that comes up and comes up again and again. I would actually be nice to have a solution to this that we could actually have an annotation in the spec that says, "Here, user code will run," and it would actually be even nicer if this was enforced. Like, I was immediately remembered— reminded of a, basically in v8 we have a "does not execute JavaScript" scope, and if you open that scope and then JavaScript runs, then that is a— an assertion failure, basically. And I don't think that would be a good fit for the spec, but basically the opposite would be nice, basically "Here, you are allowed to run." Right? + +MAG: Unfortunately, this does exist. Yeah, okay. If you open the spec and hit U, little red dots show up, or little red— I think they're exclamation points— show up being, "This operation can call user code." + +KG: It's a box that says UC, which stands for user code. + +OFR: Okay, and why can we use that here? Then? Like, then it's broken, or? + +KG: So, it assumes that when you are calling, for example, `[[GetPrototypeOf]]` on an arbitrary object, that this can run user code, because it can if you give it a genuinely arbitrary object. This is a case where we are sort of switching on the type of the object, and, like, we need to know how to do that. The spec doesn't know how to do that. + +OFR: Okay, I see what you mean, but basically we could have, like— like, basically the thing would be, okay, if you put— yeah, okay, I see what you mean. Okay. + +KG: Yeah, the annotation does require a small amount of manual work, specifically basically for these internal methods, where, if you are doing a `[[GetPrototypeOf]]` on something that you know is, either an ordinary object or a typeDeray, then that doesn't call user code, and otherwise it does, and, this— there's a small amount of manual work for maintaining it. So it doesn't— it doesn't save us here. + +USA: Next, we have JSL who says plus 1 to advancing to stage 2.7, but would like to see draft spec text for the host hook this week if possible. And next, we have CDA. + +CDA: I just want to be clear, about what the expectation is here for the editorial/kind of sort of normative, not really spec change in that: are we proposing to advance to 2.7 full stop, and then, the spec text stuff is nice to have, or is that, conditional for advancing, which, would therefore include sign-off of reviewers as well as editors group? That is a question for JSL and maybe some others, KG? + +JHD: I think it requires spec sign-off. It can be conditional on that spec sign-off, but you can't just, leave it— have a nebulous spec change pre-approved. + +CDA: Well, I'm not suggesting that the nebulous spec change needs to be pre-approved, I'm just wondering, is it— are we wanting to make sure that that change gets approved before advancing to 2.7? + +MAG: So, maybe the actual answer here is that can we schedule a continuation for this for either tomorrow or the day after, for, three and a half minutes, and then I'll make sure that we have new spec text for that day, and we'll just go for there, and then that will be the actual advancement? + +CDA: Yeah, we'll schedule a continuation just gentle reminder that you need to make sure that you've got the editors group and the reviewers, I think is JRL and JHD, signed off on the spec change. + +MAG: Yep, I'll look at it. + +USA: Okay, then next we have MF who says share with editors ASAP so we can review. And that's it for the queue. + +MAG: All right. Thank you, everyone. + +### Speaker's Summary of Key Points + +* User access was removed +* Spec text is written and reviewed. +* Some question of how handle internal methods being overridden; want to avoid being too tight to covering cases which are safe (e.g. TypedArray) but still handling the possibility of arbitrary host overrides +* Host Hook proposed and taken up. +* Encouragement to get this to 2.7 this week. + +### Conclusion + +* Stage advancement deferred to continuation on Wednesday after Host hook is written + +## Secretary's Report + +Presenter: Samina Husain (SHN) + +* [slides](https://github.com/tc39/agendas/blob/main/2026/tc39-2026-029.pdf) + +SHN: Thank you. Thank you for adjusting the agenda so I could present at the latter part of the presentation. I would like to give us, per usual, a report from the secretariat and some additional information based on some activities that are taking place this year. + +SHN: I'll do just an overview, give you some update on what has happened at the general assembly, also some recognitions that we have received and are giving, and some outreach, and then the usual annex information, which may be of interest. I haven't shared stats with you all in some time, so I just got two slides. As you may remember, I had mentioned earlier—in my presentation— the last plenary, or even before this year, ECMA celebrated 65 years, and I was looking back at a number of the work items that's been going on. So I did some stats for the last 10 years. Now, these are stats based on what we get from Google Analytics. They're not very elaborate, but they're just one variable that indicates some of the— some of the relevance, or some of the use of the work that goes on with different TCs. And so the top five ECMA standards downloaded over the last 10 years, are just highlighted there, and interestingly, of course, and nicely to be seen that you see ECMA 262 and ECMA 402 quite prominent. Not 100% sure why 2017 has such a peak, but the others seem quite reasonable and that is also quite consistent with numbers across other standards. So I think that's just a good reflection, it's one good reflection of the work being done in this technical committee. I also always do a top 10 downloads of just the year, so I did it for this first two quarters of 2026, and of course, interestingly, the same five standards that have been the top in the top five for the last 10 years continue to be in the top also in the recent years, and you see the certain performance there of ECMA-262 and ECMA-404 in the first two quarters of 2026. + +SHN: I also share these slides with the general assembly, also to give the broader community and members an indication of the work being done in different committees. For approval, so on June 20— I'm sorry, on the 30th of June, we had our general assembly, where we approved five standards. I have highlighted the two TC39 standards, in orange, so congratulations to the committee for the work that continues so that 17th edition of 262 and 13th edition of 402 were both approved unanimously by the general assembly, thank you as well for giving the presentation by the chair's report, and also to recognize the work that's being done throughout the committee. We also had a few other standards that were approved and they're listed there, 7th edition of the ECO declaration, 4th edition of 419, which comes from TC53, and last but not least, the 1st edition of the technical report coming from the newly formed TC55, and many few on TC55 are also part of TC39, so I wanted to recognize that, so congratulations also, to TC 55 for the work done for the first technical report. + +SHN: Also approved at the general assembly were some new members, I wanted to bring to your attention that AMD has joined ECMA, NVIDIA has joined ECMA, very large organizations, also very good, they're working predominantly for both of them in TC 57, which is the high-level shading language, that's going quite well, so a newly started, so it's great to see these members here, and hopefully we will attract a few other members from the same community to work on 57 and more. The other two members are not-for-profit, Erlang Ecosystem Foundation, they're in TC 54, and I have just made an error, University of Illinois, is not TC 54, it's TC 56 on the AI topic, so I will have that corrected. Okay, that was just to give you a general view of what has been going on with work in ECMA, and also the work, the approvals that took place at the general assembly. + +SHN: Next, I would like to just take a moment and I want to recognize a member of TC39 who has been very active; their leadership, dedication has really strengthened ECMA TC39, in many ways, and they were, of course, by TC 39 promoted for an ECMA recognition award, and I want to recognize KG for his work over the many years. KG— this is your citation by TC39, to recognize you for the work you have done over the many years. I'm going to say it's almost 10, I could be wrong, but I know you've been active for many years, and so first I want to congratulate you for your exceptional dedication, your work that you have been doing, and I know now you are an invited expert for TC 39, and I will pause here to give the committee a few moments if any member of the committee would like to also make a few comments, to recognize Kevin for his work, but maybe you can just open your mic and say a few words. + +KG: Thank you, I appreciate all that applause emojis, thank you very much. + +CDA: Yeah, I'll be really brief, I'll try to kind of echo what I said about this at the GA when this came up, which is that this— what you see on the screen here is a pretty understated way to describe all of KG’s contributions to the committee, that I think most everyone here are probably already familiar with, so you know, really thank KG for all his work on the committee in the past, and in the editor's group, of course, and for all the other things that he does, the bot, the transcription bot he's responsible for, the archiving of chats, and that's to say nothing of all of his work on proposals, and all of that, so yeah, thank you KG, and looking forward to your continued participation and contributions in the future. + +SHN: Thank you, Chris, that was very well said, and thank you for bringing that up, and you also did share that at the general assembly, and it was very much recognized. + +CDA: We do still have some folks on the queue, there is support for KG from EAO, and MF, and then AKI is on the queue. + +AKI: I wanted to take a second, I also said this, or some form of this at the GA, I assume I didn't record myself, but I wanted to mention that KG, you have a patience and grace that is impossible for some people, and that's no shade on some people, I am some people. Kevin, your ability to hear somebody out, even when they're saying something that's completely bonkers, and then help them get to where they're trying to get is really something special, and it's something I have always really appreciated about you. + +KG: That's very kind, I can recommend to anyone, spending a couple of years being a teacher's assistant for intro classes at college, because it sure does teach you patience. + +SHN: Well said. + +MM: So, so first of all, just enthusiastically agreeing with everything said so far. The additional things I want to really focus on is that you have extraordinarily good taste when it comes to design decisions about programming language, and it especially makes a difference for JavaScript, because you know, for over decades now, we've been evolving from a starting point, which is incredibly messy, and trying to guide this incredibly messy language towards becoming a language shaped by better tastes while not breaking the web is an amazing challenge, and it has been wonderful to have your design tastes be a guide on that, and that combined with your rigorous detailed orientation the two of those things together are just a very, very powerful combination, and the result is that language is much better than it would have been without your contributions. + +KG: Thank you, MM, that means a lot, especially coming from you. + +USA: I think that's all. + +SHN: thank you very much. From my side, to share all that for Kevin, because as Chris said, this slide does not state enough, absolutely not, so again, congratulations Kevin for all. We look forward to your continued contribution, and I do look forward to meeting you personally soon, so we can share the award with you personally at a plenary. KG, would you like to say a few words before I go to our next slide? + +KG: I look forward to continuing to contribute. Yeah, I, well, you know, pending my continued ability to participate in the committee, I guess, because I'm enjoying retirement currently, so, we'll see what happens. + +SHN: You're far too young, far too young, but it's great, stay active. Okay, thank you very much, and thank you to everybody for bringing this contribution, for bringing this citation so we could award this to KG. + +SHN: We have a few other recognitions in a different way, that I also want to bring up for everybody here. This year ECMA celebrated its 65th anniversary, and doing so we had a meeting here in Geneva and a small celebration amongst the different contributing organizations and members and some local activities. In this, we received a recognition, ECMA received a recognition, I've taken a picture of it there, from ISO IEC JTC 1, as you have heard many times, we talk about ECMA's relationship with JTC 1, its long-lasting relationship as a category A liaison, which is the highest level of relationship you can have with this JTC 1, where we have been publishing a number of standards, 220+ to be exact, on the fast track, so work done at ECMA has been published at JTC 1, it comes into an international channel, it gives it very high visibility, but not only that, JTC1. On the 65th anniversary they gave us this beautiful glass award, and wrote a citation to which I have summarized here a few bullets in which they acknowledge—of course—our very longstanding work that we've been doing for 6 decades at ECMA. The relationship we have at JTC1 for nearly 40 years, and how they appreciate the contributions from the ECMA Technical Experts into the work of JTC1, but also very importantly, last but not least, is the commitment to continue the collaboration. So this is something that we will want to continue to build from ECMA and JTC1 for also all new standards, new technical committees, and, we foresee that this will be a strong bond with work coming in for more programming languages, perhaps other software, IT work, cyber resilience act has many different aspects and one of the ones that we'll be bringing here is TC54, CycloneDX, so very strong relationship and a very special recognition that we received from JTC1, so I wanted to share that with everybody. + +SHN: So we've also got still more recognition coming up, and I don't know if everybody's aware, but I'm guessing you all are aware about the next one, which is that it will be 30 years that ECMAScript has been published. It'll be 30 years next year, that ECMA Script has been published. The last 10-plus years has been the cadence of the annual release. Prior to that, it had different years of release, and you have a great community that works. You saw in my initial slides some of the statistics that really showed the strong prevalence of this of your work and how well it's used in industry. So we would like to do some celebrations next year, and I would love the support and input from the committee so if you have an idea, if you want to come together with a few with a small ad hoc or a small committee or just a few people who may like to create a new logo or a logo that commemorates the 30 year, for TC39, which we can then use and also have it on our website on GitHub and many other items on the maybe the front cover of the standards page if I can get it there. I would also like to celebrate this event as we have the different hybrid meetings coming up in 2027. I do not have an official date and location as of yet, but maybe that's all been identified. It'd be great if you would share that with me, the chairs and then we can do some planning. + +SHN: I'd also like to do some swag and so of course if you all have ideas throw them in my direction or in Aki's direction and we'll see what we can do and USA I want to first thank you for your blog that you have on that you do and you've recognized ECMA and the work that ECMA does and how relevant it is for the work. Perhaps your blog we can share we're going to do some new things on the ECMA website or we can create a new blog to really recognize the many years of successful work of ECMAScript. So this is to recognize next year's 30 years for ECMAScript. It may also be 10 years for our royalty-free alternative copy alternative option which is very relevant in the industry very relevant in standards organizations so we may have a reason also to recognize that but I have to double-check that but I want to share this with you and I look forward I really would appreciate some feedback which you may share with me on today's call or at a later time. + +SHN: Another thing that we are doing in the secretariat together with not only just the immediate secretariat here who is based in Geneva but we do have a number of different colleagues that are supporting us. Aki is one of them. We have Brian McAuliffe, who used to be previously with HP. He's now joined ECMA for a period of time to really help support some strategic plans. And we have other colleagues who are involved in work that's going on in the European Commission so all of that brings a lot of strength into ECMA but we also want to hear from the members, the participants on how we can collaborate better. I've written some things out here on the benefits to consider which are very much interested by a couple of other TCs, predominantly TC26. I do not know if it is of interest for TC39 it may not at all be, but maybe there's something else but I've written it down there that if there is a thought on that I would appreciate it. Then of course we want to add more visibility. I've mentioned the blog we need to improve on how we work on social media and which social media links, that is not only LinkedIn. And then of course to really have some more initiatives that we can engage and so we can continue to build on the interest of the work we're doing. So just some feedback requests based on the things that we're doing you have these slides you can easily get them and think about these questions. I would appreciate feedback. I would like to start to make differences for each of the TCs. We're already doing it for TC26 and TC20. We do many things for TC39. I'd like to do more. Please think about some of these questions, give me some feedback, and we'll see what we can do to also generate more visibility and more participation. + +SHN: The rest of my slides are the annexes that I always present. I will leave you to go through them. I will just of course list here the dates of the next general assembly and executive committee meetings, which are both relevant. One, the executive committee meetings coming up next on October 7th, 8th, it would be important to have a chair's report at that time. It would be nice if the chair's report provided some more details, maybe some technical one-line bullets on some of the work that the TGs are doing. I think the executive committee appreciates that and so does the GA. Just keep those dates in mind for the future presentations by the chairs. Those are your meeting dates which you all know and then there's a list of documents that are listed here. Reach out if you need to see them. I would like to see them just based on the titles. And of course the code of conduct which you probably already discussed at the start of the meeting. And then of course the rules of the invited experts and we have a number of them that are very relevant to TC39. + +SHN: Okay, thank you very much. That is the end of my presentation. I will stop sharing and if there are any comments or questions I am happy to hear them. + +USA: Thanks a lot. There's MM on the queue with an insightful comment. He says 2017 probably had a big peak or well the peak that it had because 2016 like ES2016 or ES6 or was it ES6? Well, anyway, it was a huge transition and everything's been incremental since then. + +MM: Yeah, ES ES2016 was also ES6. Yes. + +SHN: Yes, it was. Thank you for that reminder. Thank you. That brings it into perspective. Thank you, MM. + +DRO: But if people would like to go early, that's also totally fine. I'm in no rush. + +USA: I think let's try and let's try and sneak it in. + +### Speaker's Summary of Key Points + +An update on Ecma’s recent work, including highlights from the General Assembly, recognitions awarded, outreach efforts and statistical usage of Ecma standards was provided. 30 June General Assembly approved standards and members, including: + +* ECMA‑262, 17th edition +* ECMA‑402, 13th edition +* AMD, NVIDIA, Erlang Ecosystem Foundation and University of Illinois (members) + +Recognition of Kevin Gibbons (KG). Ecma appreciated KG with an Ecma Recognition Award for exceptional service. Delegates praised his leadership, collaborative spirit and contributions. + +Ecma’s recognition and commemorative glass award from ISO/IEC JTC 1 was shared. + +2027 marks 30 years since ECMAScript was first published, and celebrations are planned. + +## Map take for stage 1, 2, or 2.7 + +Presenter: Devin Rousso (DRO) + +* [proposal](https://github.com/dcrousso/proposal-Map-take) + +DRO: Yeah, I don't. I put all the slides basically of things that I was going to talk about in the proposal. Basically, hi, my name is DRO. I'm an invited expert. I this is a common operation that I do very, very frequently writing JavaScript code for things that are pending. You know, you have a map of callbacks for an identifier, a map of in-flight requests for an identifier, more often than not, I'm not using a map as a long-term storage. I'm using it as a short-term storage where it is a way of relating some data to an identifier for a temporary purpose. And I'd love it if there was a way to be able to more efficiently get and remove the value from that map because more often than not, rather than trying to just get the value and modify it, I'm using that temporary nature of the storage to just have it be there temporarily and once I need it, I'm going to remove it from the map. So rather than doing two hashes, I'd like to sort of see if there's a way where we can just do a hash once, save the value, and then remove it from the map. That's sort of the basic gist of it. The name I took `take` because that's what I've called it in the past and what I've seen it be called elsewhere. I also just realized maybe about 15 minutes ago that we already have iterator `take`. So there's a little bit of benefit there, but I'm also totally fine with things like get or remove or get or delete, get and delete to match, get and initialize or get or initialize. Those kinds of naming things. Currently, I'm only proposing it on map and weak map given that those are really the only ones that actually have a key value relationship, but I also would be open to the idea of putting it in set if that's something that we feel would be valuable. I don't think of it that way, but in the same way that we have values and keys and entries on set to sort of match map, I maybe this would be something that would be valuable to keep the signatures relatively interchangeable. + +DRO: So there's a bunch of examples in the code here. I'm realizing that we only have 10 minutes, so I'm just trying to, sort of run through this. The only sort of weird thing about this as opposed to the previous get and then delete is that in the case of this take method, it's really not easy to distinguish whether or not the key existed if the value for that key is undefined. So that would be the second row in this table, for example. I think in the case that a developer really needs that kind of behavior, they can always fall back to using has first to determine if the key exists and then delete it after the fact or look at the return value of delete to know whether or not something ex or excuse me, get and then delete the old path and then look at the return value of delete to know whether or not the key existed or not. If this is something that we feel we want to address, I'm also fine to change the return value of take to be an object that has like present and then value as a way of giving you both pieces of information, but I personally have never found a use for that. So I thought I would just keep this sort of simple and not have it do an object with a present key and a value key and those kinds of things. There's a lot of prior art with this in other languages and things like Rust, Python, a lot of libraries that I've seen in C++ for maps, including somewhat the standard library have equivalents of something like this. We even have something similar in JavaScript array prototype pop. And again, iterator take. So there's a lot of prior art for this. Many languages use take. Some use remove. Some, unlike JavaScript, have .`delete()` and `.remove()` actually return the removed value as opposed to just a true or false. So this is not an unfamiliar concept. And it's used somewhat frequently from some of the basic counts that I was able to find as well. It's a pretty common thing, not unbelievably so, but it has quite a lot of usages. And here's a couple random examples just of sort of what I'm describing earlier. You have a list of pending responses when the response finishes, you have the request data waiting for the response data. As soon as you have the response data, you remove it from the pending list. You have information for when, some sort of worker or job is active when it terminates or is merged. You get rid of it from the map or you have extra resources that you're trying to clean up. All sorts of very common operations that I've seen across a number of different code bases. And it's pretty similar simple to polyfill because ultimately this is really more of an optimization for a common operation, let alone some new semantics. So the polyfill is basically you just call get and then you call delete and then you return the value from get. It's not anything super complicated, but the hope is that similar to things like get or initialize, it provides a optimization for common operations that, I see and do frequently. So with that being said, I know I've used 5 minutes. Hopefully, we only have five minutes worth of discussion. I'm hoping for stage one, stage two, stage 2.7, anything of the above. And I have written some spec text for this as well that I'm happy to show very quickly. So there is some basic spec text in these describing what it how it operates as well. + +KG: I guess I'm first on the queue. I support this advancing under some name other than take. There's an open issue about this, but the fact that iterator take exists and is so different means that I think take is just not a viable name. I the readme says get or delete, which I'd be fine with, or someone suggested extract, which I'd also be fine with. Basically just not take. + +USA: Should we go through the queue real quick or should we just go through it and. + +CDA: I think we should take our time with this. I actually regret asking if DRO could go because once he flashed up the proposal, I was like, I remembered which one it was and I felt like the time box was probably undersized already from the amount of conversation that I was expecting. And I think the queue reflects that. So we could maybe just go to the top of the hour and then do a continuation and because we shortchanged Devon, we'll put this as the kind of next available barring any constraints. + +CDA: Okay. Maybe we can get to MM's topic. + +MM: Yeah. So you said you were you indicated openness to having it on sets too, but said you don't think of it that way. So that surprised me because I don't know of a way to think about it that would not include sets. And in any case, I want to express a preference that either it's on both or neither. That having it on maps and not on sets seems very surprising to me. + +DRO: If I said that it's not the way that I think about that, that was a misspeak on my part. What I meant to say is I don't ever use it for that purpose. Like I don't think I've ever needed to take something from a set as opposed to just deleting it. But I see no reason why it couldn't be on both. So I would lean like you said towards having it on both. + +MM: Okay. Okay. That's all. + +DRO: So my apologies if that was unclear. I misspoke. + +CDA: Okay. I think we should just call it here, I think, 15 seconds left. Let's stop on time. I will copy the queue. And we will resume tomorrow. Thanks everyone. + +USA: Yeah. And special thanks to our note takers. diff --git a/meetings/2026-07/july-21.md b/meetings/2026-07/july-21.md new file mode 100644 index 00000000..428b81d2 --- /dev/null +++ b/meetings/2026-07/july-21.md @@ -0,0 +1,1100 @@ +# 115th TC39 Meeting + +Day Two—21 July 2026 + +**Attendees:** + +| Name | Abbreviation | Organization | +|---------------------|--------------|--------------------| +| Chris de Almeida | CDA | IBM | +| James Snell | JSL | Cloudflare | +| Richard Gibson | RGN | Agoric | +| Jordan Harband | JHD | Socket | +| Ashley Claymore | ACE | Bloomberg | +| Caio Lima | CLA | Igalia | +| Waldemar Horwat | WH | Invited Expert | +| Bradford C. Smith | BSH | Google | +| Dmitry Makhnev | DJM | JetBrains | +| Aki Rose Braun | AKI | Ecma International | +| Samina Husain | SHN | Ecma International | +| Linus Groh | LGH | Bloomberg | +| Lea Verou | LVU | OpenJS | +| Olivier Flückiger | OFR | Google | +| Nikolaos Papaspyrou | NPU | Google | +| Guy Bedford | GB | Cloudflare | +| Devin Rousso | DRO | Invited Expert | +| Istvan Sebestyen | IS | Ecma | +| Eemeli Aro | EAO | Mozilla | +| Philip Chimento | PFC | Igalia | +| Dan Minor | DLM | Mozilla | +| Jordan Harband | JHD | HeroDevs | +| Justin Ridgewell | JRL | Google | +| Kevin Gibbons | KG | Invited Expert | +| Matthew Gaudet | MAG | Mozilla | +| Michael Ficarra | MF | F5 | +| Mark S. Miller | MM | Agoric | +| Stephen Hicks | SHS | Google | + +## Continuation: Map take for stage 1, 2, or 2.7 + +Presenter: Devin Rousso (DRO) + +* [proposal](https://github.com/dcrousso/proposal-Map-take) +* No slides presented + +DRO: did have a large queue from yesterday. Do we want to just jump back into that queue? Or do you want me to redo this? + +CDA: Let me restore the queue right now. Thank you for the reminder. Keith is first on the queue. + +KM: Very exciting. Anyway, yeah. I think you had in your slides I'm trying to page this all back in. There was a concern about undefined being a return value if the thing is not in the set. I suppose one thing that I didn't see and maybe could work is passing in a third parameter or I guess maybe a second parameter in the set case. That is like what value to return if the element is missing from the set. And then you can have any value you want, and it would default to undefined. You could pass a special custom Symbol if you want to differentiate from anything else. Yeah. + +DRO: Yeah. That seems like a potentially reasonable thing. I'm not opposed to adding some behavior like that. + +KG: I 100% agree, but this is at least as serious of a problem with the existing dot get method, which is going to be called 1,000 times more often than this. If we are interested in solving the problem of "map lookups don't tell you when the thing you are looking up was missing", I am all about solving that problem. I don't think we should solve it only for this method. I think that would be kind of silly. + +DRO: I also agree with that. + +MF: Yeah. I think that conversation between KM and KG mostly is getting at what I'm looking for here. But you also mentioned for the undefined case, the possibility of returning an object. I think that is overkill and we should try to keep this lighter weight. I'd rather not see a bunch of small objects created all the time with this. + +MF: And moving on to my next topic. On the naming, again, I agree with KG's position from earlier. `take` is just not going to work for us partially because of `Iterator.prototype.take`, but for other reasons as well. I think `getAndDelete` is a great name. Not that I want to bike shed names in this meeting. + +CM: the existence of iterator take argue strongly against using that name for this. + +JSL: plus one for stage one. The rest can be figured out. + +KM: Yeah. I think this kind of has the same argument that we had, I guess, for get or insert. That it would be a performance when I don't think that's fundamentally true, since I think it's relatively straightforward for engines. To optimize this, even in not optimizing compilers by simply just like remembering the last hash bucket you looked up, and then when you look up again, you just check if it's the same one. If it were really that hot, and then certainly in the optimizing compilers, engines will identify that you looked at the same hash bucket twice, and in a row without anything that could change them in between, and do like a standard compiler optimization. It's like common subexpression elimination. To identify this exact same bucket, and then you can use the same bucket. That said, I'm not saying we shouldn't add this function. I'm just making the point that I don't think this is a performance win. + +WH: I support this. Like MM, I would also want the analogous capability on sets. + +MF: We heard a couple of people with support for adding this to Sets. I think that `set.delete` is already sufficient here. It returns a boolean. What more would this give you over that? And to be clear, the boolean tells you whether the key was in the Set. + +PKA: Sorry. I was going to add on to that, especially if the name is get and delete. You know, there's no get on sets, so. Feels a bit odd. + +CDA: All right. That is it for the queue. + +DRO: Kevin, I guess in that case, well, I guess I can say that if I rename this to getAndDelete, I was originally proposing this just on map. So it seems like there's some people who want it also on set, and some people who think that that is unnecessary. I'm really fine to go either way on this because, like I mentioned, I don't think I would ever use it on set. But yeah, I would love to ask for a stage advancement, I guess, as far as I can go. Assuming that I rename this with the understanding that I rename this to get and delete. + +CDA: I'm on the queue with I don't think we have to figure that out for stage one. We absolutely need to figure that out for stage two. But unless folks feel differently, I don't know that that distinction is critical to ask for advancement here. I see NRO and ACE are entering the queue. + +NRO: Yeah. Could just the people that have already expressed support for the Set method restate the use case for like what would expect the API to do in sets? + +CDA: I don't know. I'm trying to look back at the notes and folks who wanted it on set. + +DRO: I want to say I think the two people that I can remember would be Mark and Waldemar. + +NRO: Yeah. I'm looking at notes. It just said they would like to have it, but I cannot find in the notes or remember why I would want it. + +DRO: My guess from what I remember is it would be nice to have for like what's the word I'm looking for? Similar API search. + +KM: That was like symmetry. + +DRO: Thank you. That's the word. Symmetry is the word I was looking for. Thank you, KM. And sort of the same way that we have keys and values for sets that don't entirely sort of somewhat make sense as well as entries, we could also have a similar API surface. + +RGN: Not to advocate for or against it being on Set, but the behavior for it, I do know what MM at least would expect, which is that the input key if present is returned right back. And the application for that would be point-free programming where you don't actually have the key in a variable and you don't care about it unless it was actually removed from the set. + +NRO: Yeah. Just for the symmetry thing, I want to point out that get doesn't exist on sets. So it's not symmetrical to add get or delete or get and delete. + +ACE: Stage one sounds good. + +CDA: And that is it for the queue right now. + +DRO: I mean, I guess my question would be as far as the symmetry point on also wanting this on set, is that something that is necessary in order for this to advance? Because I would like to take this. I don't want to have to wait another couple of months to present this again for stage two if I'm all the only difference is going to be whether or not it exists on another prototype. So if possible, I'd love to just I mean, I'd love to ask for stage two or stage 2.7 with just on map and weak map if that's something that we are okay with. + +WH: No, it's not necessary. Given that we don't have get, and delete already returns presence, it's not important. + +CDA: I'm next on the queue with it's not a stage one blocker for me. I'm less enthusiastic about going to stage two right away. I'm not sure that I feel that strongly about it. Ashley is next on the queue. + +ACE: In terms of I don't think even though it might be feel sad to only achieve stage one, I think in reality, because it doesn't stop people writing tests and implementations and spec. So those things can all be in line. We talk offline to agree. Naming and things. And then next meeting, go to 2.7. Maybe the test 262 PR has already opened. I think it's cool. I wouldn't see stage one as a sign this is going to be a super slow process. + +MF: Yeah. I think the main open area of design exploration is when you pass undefined how you can tell whether it was present or not. And you know we talked about some possible solutions that I don't think we liked, but I would like you to take the time and actually explore what that solution space could look like there. There may be other things that we didn't think of in the 10-minute discussion that we've had here. So I don't think it's ready for stage two yet, but I also agree with ACE that once we have figured those things out and we all seem happy with the API name, it can go straight to 2.7 from one at the next meeting that's presented. + +DLM: On the queue with support stage one. + +CDA: I'm next on the queue and I just wanted to point out going directly to 2.7 in a single meeting has specific challenges, which are that we the committee has to assign reviewers at stage two. So we would have to assign reviewers at that meeting. Those reviewers would have to review and sign off at that meeting. And the editors group would also have to sign off all in the context of that meeting. There's precedence for that happening. I just want to be clear on what that would entail. + +ACE: I pre-volunteer to be a spec reviewer. + +CDA: All right. Well, let's remember that for stage two. + +DRO: All right. So then I guess with all that in mind, I want to I'm pretty sure I've already gotten quite a lot of support, but I guess official request for stage one. + +CDA: Yep. You definitely had several voices of support for stage one. Those folks feel free to express their support again. And other than that, you know we'd like to know if anybody objects to stage one. Michael's on the queue with support. Not seeing anybody entering the queue. ACE is now on the queue. With support. Did you want to speak? I will take the silence as a no. All right. Congratulations. You have stage one. Thank you, Devin. + +CDA: All right. Now, we have to find a new note taker. Or two. Can we get somebody to help with the notes, please? We are losing Ashley. Or because Ashley is one. + +### Speaker's Summary of Key Points + +* Add new method for `Map` and `WeakMap` that allows for retrieving the value and deleting the entry for the given key in a single operation. + +### Conclusion + +* Accepted as Stage 1 +* Rename to `getAndDelete`. +* Not added to `Set` or `WeakSet` since there’s no equivalent `get`. +* Explore more options for differentiating “key is present with `undefined` value” vs “key is not present”. + +## JSON.parseImmutable + +Presenter: Ashley Claymore (ACE) + Peter Klecha (PKA) + +* [proposal](https://github.com/tc39/proposal-json-parseimmutable) +* No slides presented. + +PKA: Sure, thanks. Yeah. So hello. I'm PKA from Bloomberg. With me is ACE. We don't have slides. This is an update on the stage two proposal `JSON.parseImmutable`. Just as a reminder, some background: This was originally a companion proposal to [Records & Tuples](https://github.com/tc39/proposal-record-tuple). The idea being that this was a utility function that would parse JSON and return a Record/Tuple version of JSON. Records & Tuples has been withdrawn. However, this proposal lives on. It is living on with the idea that maybe it's still useful to have a function which parses JSON and returns a deeply immutable object. We discussed this two plenaries ago and the committee seemed generally warm towards the idea of continuing this proposal in particular under the guise of a function that returns frozen objects using the existing concept of “frozen”. Since then, we've opened some issues and my purpose here today is just to go over the resolutions or lack thereof for those issues. + +PKA: So we will just dive right in. First of all, [there was a naming question](https://github.com/tc39/proposal-json-parseimmutable/issues/2). Especially in light of the change of scope, immutable is maybe not necessarily the best term. Given that this is returning a deeply frozen object, we the champions feel it makes a lot of sense to go with `JSON.parseFrozen`, as the name for this. So I will just pause a moment if anyone wants to cry out against that choice. I see EAO's on the queue. + +PKA: Does this concern the naming question or is this a separate matter? + +EAO: I'm not sure if these are the same question or if they're going to be separate, but frozen sounds better than immutable. And I would have a strong preference for `JSON.parse` with an options bag where you then say “frozen: true” rather than `JSON.parseFrozen`. I don't know if this actually relates to something you're going to say later. + +PKA: It's not something that I intended to bring up today. It does logically relate to the question of the name since your proposal would obviate the need for a name. It’s an interesting question. I don't know if we had thought about that. Adding an extra option and keeping `JSON.parse`. I think that maybe it did come up. + +EAO: I think there's an issue for this, specifically. Or at least it's covered in one of your issues. + +PKA: Okay. Noted. I don't have an immediate response to that. I don't know, Ashley, if you want to jump in. + +ACE: I think one of the reasons is like you could imagine a TypeScript interface for this. We didn't like a parameter so drastically changing the semantics of the return types. Because that type of overloading isn't really easy to express, and, I think, makes it slightly harder to reason about that. Personally, I'd rather two well-named methods than one method that takes a boolean just as a general design. + +EAO: So yeah, just noting that TypeScript can entirely well differentiate the type of the output based on something like “frozen: true” in the options bag provided that it's a literal. Given the changes here, where the intent is, as I understand it, to return objects and arrays that are frozen, their type is not really very different, as was the case with Records & Tuples, from where this story here started. + +PKA: So there certainly is a TypeScript annotation for a read-only object. And it is definitely one of our goals at Bloomberg; something that we really want out of this is static analyzability of this function, that it is returning specifically read-only data. + +KG: Just continuing the name bikeshed: “parseFrozen” seems a little weird because it sounds like it is parsing a frozen thing rather than parsing to a frozen thing. So I wanted to throw “parseToFrozen” in the ring. That said, option also sounds good to me. + +PKA: Yeah. I don't know if “parseToFrozen” came up. I do think that that is a perfectly good choice. + +LGH: I think I've mentioned this in the past, but I would like if we could—whatever the solution is—also have this for JSON modules via an import attribute. At Bloomberg internally, we do this by default. So when we import JSON modules, we return them frozen. Against the spec. So it would be nice to have a way to sort of choose which way to go. No strong preference for the standalone API, but it would be nice if it could be mirrored. + +CDA: That's it for the queue. + +PKA: Okay. So maybe a little more discussion to be had about the name as well as the question of single function versus multiple functions. Next open issue, [should the return objects have a null prototype?](https://github.com/tc39/proposal-json-parseimmutable/issues/6). This was brought up at the last meeting as well. Our preference is to have a null prototype. Makes the object just a little more frozen. And we didn't really see any pushback on that in the issue. So once again, pause to allow the multitudes to cry out. + +KG: Oh, I hit the wrong button on the queue. Sorry. Yes. No prototype sounds great. There are, as I'm sure we all know, so many bugs—many of them security issues caused by people being able to walk to the function constructor from a parse to JSON object. I think if this existed everyone would immediately suggest doing this as the default for all JSON parsing. Just to avoid that specific problem. That said, I'm not totally sure about whether it should only exist for frozen. If the way this works is that we just have like a second parsing method that gives us nice things and then we just use the one that gives us nice things everywhere we can, I'm okay with that world. But it seems a little more natural to just have a separate option for the existing parsing method that says null prototype and maybe a separate and totally orthogonal option to the existing parsing method that says give you frozen objects. It seems a little weird to conflate them. That said, I'm okay with that if that's like the only way we can feasibly do this. + +MF: Strong preference for null prototype. + +ACE: Maybe no one's thinking this, but just because—technically obviously—arrays are objects and just in case anyone was thinking we're saying that parsing a JSON array would also return an array but without a prototype—that's not what we're saying. This is only for JSON objects not JSON arrays. + +ACE: Having two options, I kind of like the idea of yes you could separate out having a null prototype from frozen. Though I do feel like maybe that is just us cheffing thing rather than it being widely useful. I can't imagine someone caring about one and not the other. I feel like in reality we would always see people choose both. But maybe I'm wrong about that. It's just my gut feeling. + +KG: I agree that the majority of cases will not need to separate them but it's not that unusual to mutate the result of your JSON parse. Like I do this a lot in cases where I care about allocations. And I would still like to avoid handling things with non-null prototypes that might come from user input. So I don't know. I think it is pretty reasonable to be mutating things and even in the world where you're mutating things a defensive null prototype is still valuable. + +ACE: I guess—that's good to know. And yes, I do see that. It would be a shame that the majority case would have like to say I'd almost want somewhere you'd opt out rather than opt in if we think the majority are going to say true to both. I'd rather that was like the easiest thing to type and the more niche option of null prototypes not frozen was the more verbose one to type. I can't think of an actual design. I mean, I guess we could have any num as opposed to boolean flags where we just have some well-defined strings for the flavor that maybe that's terrible in a different way. + +EAO: You might have just said this, but effectively I think that it is a highly unlikely that somebody would want to have frozen and not to have a null prototype on the object. But the other slots in the grid kind of do make sense. + +CDA: That is it for the queue. + +PKA: So, much to consider there as well. + +PKA: [Our final issue is about the reviver](https://github.com/tc39/proposal-json-parseimmutable/issues/7) and this is a situation where we have maybe more to discuss. There are a lot of logical possibilities about how we could imagine the reviver would behave here. As I mentioned before, we at Bloomberg care a lot about static analyzability here. So one option that we want to reject is the possibility that you could have a reviver where user code returns non-frozen objects and that's just accepted. We want this function to statically always return frozen things. Within that, there are still various options. The one that we sort of weakly declared in this issue is for `JSON.parseToFrozen` to throw if the reviver returns anything other than a primitive or an object that it was not passed. The idea being we know primitives are immutable and if you return the object you are passed as the reviver, then we know you are frozen because you were frozen by the engine and passed into the function already frozen. That achieves our goals. And it's a very simple thing to spec. + +PKA: Two other options that we have are to simply not have a reviver at all. Obviously we do know there are use cases for a reviver. So that would rule them out. Like for example, handling bigints. It would also not take advantage of the recent source text proposal for revivers that landed here a little while ago. On the other hand, we could have a reviver which does allow you to return any object you like, for example if you have some custom scheme for serializing circular objects and you want to hydrate certain strings into objects. We could allow that where the engine validates that the object you return is deeply frozen—for some definition of “deeply frozen”. This would be definitely more complex to spec. We would have to agree on first of all what “deeply frozen” means. And add a procedure for determining deep frozenness to the spec. But it would give the greater functionality. There was some discussion in the issue here and something a point that ACE made is that if we have this more limited capability for the reviver or no reviver at all, it's not like that removes any expressiveness from the language as it is you can already JSON parse and create something that is deeply frozen by doing the freezing yourself in a reviver. We're just trying to create a happy path; creating a clearer, possibly optimized way of doing that. So it's not as though we're removing any functionality. By not having a reviver or by having a limited reviver. But it's kind of a judgment call about how much capability do we want to provide and how messy with the spec are we willing to get. So I'll pause for further input. + +KG: Yeah. I don't know how I feel about the general topic of the revivers yet. But just a quick comment. So the way that the reviver works right now is that it's the second argument to JSON parse and it must be callable. If you pass something non-callable I forget if it's an error or if it's just ignored. If we did go with the options approach rather than the separate method name approach the obvious way to do options would be to pass an options bag as the second argument and then it would just take the place of the reviver. And if we think that you don't really need a reviver for this because it doesn't add any expressivity—another way of thinking of that is that this is a shorthand for a particular kind of reviver which is basically just a reviver that freezes all of the objects it returns. So you could say that you are just passing an object here in place of passing a reviver, that is sort of specifying the reviving behavior that you want. And then you very naturally cannot also provide a reviver because the options bag is literally taking the place that the reviver would have. + +ACE: I love it. + +PKA: I'll just add it would also be pretty easy to tack on a reviver field to that options bag if we wanted to. + +KG: Well, yes. But I mean it would— + +PKA: Like if you want a null prototype but not frozen for example. Might you then also want a reviver? It seems like you might. + +KG: Well, I mean the thing that I am imagining is that you have an options bag that has say two options one of which is “null proto” and one of which is “frozen”. And you just specify those in the options bag. + +PKA: For sure. Yeah. Yeah. I mean what you're saying is coherent. + +KG: Yes. I agree that we could additionally have a reviver option in the options bag. + +PKA: Yeah. + +EAO: I support object as reviver. + +ACE: I said I love it. To expend on that. So one other thing I was thinking about in this design space was one of the motivations for this is the performance gain. Like right now passing a reviver takes you off the fast path. I was imagining another design we could have is like we have some built-in revivers with some semantics and you know engines could—the spec wouldn't say this but engines could see "oh you're passing in one of these well-known functions I know exactly what it's going to do so I don't actually literally need to call it". But the options bag is a way better way I think of expressing that especially because I couldn't think of a nice way you could compose these like static functions in a way that would work because of just their shape of key value to value. They don't necessarily chain. So yeah. An options bag in place of the reviver sounds good. I could also imagine a proposal that builds in some other of the common patterns we're starting to see such as like the bigint instead of people over and over again writing the reviver that looks at the source text that does the BigInt thing. If we see that pattern become super common we could maybe say that's one of these built-in reviver flags so you can still hit a fast path when you want that logic. I'm not proposing that right now but yeah. I think that's a much better design. So I thank you KG for bringing that. I can see why you're winning awards in this committee. + +EAO: If we are approaching this as providing a way to define a reviver through an options bag it would probably make sense for the defaults of that option bag to be the preferred behavior and therefore rather than considering names like “frozen” or “immutable” it would I think make more sense in this approach to have the option there be “mutable” that you would need to set to true rather than the default false in the options bag in order to not opt in to getting frozen behavior out of this approach. + +PKA: So does that mean that passing an empty object would produce a frozen result but passing no second argument at all would give you a not frozen result? + +EAO: If we make it so. I mean I could also buy the argument that we would require that options bag to have at least something in there because we also have this other approach of having the wanting to get the null prototype so you would need to opt into something in the options bag but this is details and I don't really have a strong opinion on where those go. + +PKA: Yeah. I feel like given that the default is already to produce something mutable it feels like we can't escape having the default option also be mutable. I mean if we just made a whole new function `JSON.parseConfigurable` or something. That's not the right word. + +EAO: JSON.parse2 + +PKA: Yeah. And you know then we could then we could you know decide new defaults for things but I'm not sure that's on the table. + +KG: Pretty much just seconding what PKA said. I think that the defaults have to be the current behavior. This is also a more general web platform design principle for options bags like omitting an options bag has to be equivalent to passing an empty object for the options bag and you know specifying any option with false in the options bag is supposed to be equivalent to omitting that option from the options bag and so forth. So I think that is what users expect. It's written down as a web platform design principle which doesn't necessarily constrain us but I do think it's generally good to follow the design principles that other parts of the JavaScript ecosystem follow, and this is a pretty widely followed principle. So as much as I like opting people into better behavior I don't think it's reasonable to have the defaults be anything other than the current behavior. + +EAO: Alas. + +SHS: Yeah. It may not be great but if it's not actually an empty object as an argument but some sort of a you know going through a function I don't know if this ruins the just in time optimization or anything but that would allow you to have good defaults. + +PKA: You're suggesting sorry. Can you… + +SHS: Sorry. If instead of just passing an empty options bag you pass in a function call. Do you give it the options bag for what kind of reviver you want and it gives you a reviver function that has those properties. And now you can have good defaults for it. + +KG: Just one last quick comment. Someone, LGH or someone was mentioning that it would be nice to have support for JSON modules for this as well. The options bag approach plays nicely with JSON modules. You could just have an "options" field in the import attributes and then pass it an object, or it isn't literally an object. It's a special syntactic form or whatever. Or maybe it is an object. I forget. Anyway whatever. You can support basically the same options that you would here as long as it's not like a function. If it's just an object then you can write basically the same thing in an import attribute. + +PKA: Yep. Great point. + +CDA: Nothing more in the queue. + +PKA: Cool. Well yeah you'll hear more from us on this topic much to ponder. Thanks everybody. + +### Speaker's Summary of Key Points + +* Discussed naming, `JSON.parseImmutable` vs `JSON.parseFrozen` +* Discussed returning objects with a `null` prototype +* Discussed how to handle the reviver property +* Discussed how an options bag approach could solve all 3 of the above +* Discussed possible relation to JSON imports with import attributes + +### Conclusion + +* No consensus was formally obtained but there was general positive reaction to moving to an options-bag API +* Work on the proposal will continue and brought back to the committee + +## Error code property for Stage 2 or 2.7 + +Presenter: James Snell (JSL) + +* [proposal](https://github.com/tc39/proposal-error-code-property) +* No slides presented. + +JSL: Okay. All right. So this is error code property asking for stage two. Stage two seven if we can get it. Just as a refresher here what this is doing is just adding this a code property along the same lines as cause was added previously. It would be installed as a non-enumerable own property just like cause. In fact the spec text just modifies the way that the own properties are installed instead of installing just the cause property. We changed this to install error own properties that cover both cause and code. The value space for code is any value any type. Typical case is going to be a string. We do have the spec text here. The editor group has already started the reviews for this. No objections if not there. The one discussion that is still that we've had some pushback on is potential conflict with DOMException that's been raised by AVK. And this just goes back to the fact that DOMException has had this code property forever as a number. It's set up as a prototype property. There is no—it's not passed anywhere through the constructor through an options bag or anything like that. Looking at that discussion kind of where I know I've landed and I know where JHD appears to have landed is that we don't really see that there is a conflict but this is one point I think we just need to make sure that if if anyone here you know feels that there is a conflict here that we need to work out before we can advance please speak up. + +JSL: But at this point we have a prototype—or not prototype but a draft set of test262 tests implemented for this. I have a draft implementation for v8 ready to go. + +\[interruption by dogs 🐶] + +JSL: Okay. So yeah for the error code property we have the spec text. All it's doing is modifying the way that that cause and code are being added. Like you know right now we have the one that just handles cause this one modifies it so it does both. We have test262 tests drafted up. The editor committees already started the editor review. No objections have been raised there. The one open question that we have really is whether there actually is a conflict with DOMException or not. This was raised by AVK. Looking at it, myself and JHD landed on that there's no actual conflict there. Given the way that the property is installed. And just opening it up for discussion. + +MM: Yeah. So just what about SuppressedError? The current SuppressedError constructor does not have an options bag. + +JSL: Yeah. This adds it. It would mean that causes there also although I wouldn't expect cause to actually ever be used for anything with suppressed error but this does add the options bag to suppressed error. So that we can add code there. + +MM: Okay. So it is the case that if cause is used in this option bag it does get added to the SuppressedError instance. Is that correct? + +JSL: Yep. SuppressedError and AggregateError are basically all database(?). + +MM: Okay. Very good. Thank you. That's all. I'm in support. + +KM: I haven't had a chance to talk to AVK about it because I saw this was on the agenda after he went on vacation and he's still on vacation for a while. It doesn't strike me as outstandingly problematic given that yeah it's an optional parameter. And it's a little unclear from Honor's position what if that solves his concern or not. That said I think I'm worried if this goes to stage two now and AVK’s concerns are still there and valid that it will be harder to make further changes as needed to satisfy him. Is it possible to do I guess I have a question for the chairs. Is it possible to do a conditional advancement on waiting for AVK to come back from vacation and then have me have a chat with him about it? + +CDA: Is AVK technically a delegate here? + +KM: I know he has been. + +CDA: What I'm getting at there is I would be hesitant to make externality—AVK is currently listed as delegate. Yeah. So that seems fine. + +CDA: I was on the queue so I'll just say my reply, which is I'm not convinced that it should be a stage two blocker anyway. I don't know. It sounds like we might have a difference of opinion there. I don't find churn at stage two as a result of this to be like a big problem if we were already at stage two. But I'll keep an open mind if folks feel differently. + +KM: I mean I suppose the issue could be that AVK's opinions could be irreconcilable, that it could have to be a number or like it's reserved for these DOM things and they can't conflict with that. I don't know if that's his position. It seems more like his concern is that it will be confusing that there will be two different meanings of ".code". But I think it seemed more like his concern that this wouldn't be exposed everywhere. But I think that might be misunderstanding the proposal. Because this is provided by users and being attached. Am I correct in that we're not expecting web APIs to provide new codes and this would only be for userland adding their own error codes to the exceptions? + +JSL: There I mean there's always a possibility that web API could define codes. Like you know you can imagine there's you know in the fetch spec there's various conditions where codes might make sense. But no cases with that are currently throwing a DOMException. Would be expected to. Right. In fetch there's places that throw TypeError or RangeError or whatever. Like those cases could use code. I don't imagine any case and this is true for like you know in user code also that's already using DOMException. I would expect any of those to ever conflict. Nobody's going to be adding new codes to DOMException. + +KM: You mean that users are not going to add their own special codes? + +JSL: Yeah. + +KM: I guess the other question is whether DOMException propagates this flag. I guess it might. I don't know. I don't remember the web spec well enough to know, I guess I should have checked that. But I don't know if DOMException propagates options bags through the web through the TC39. Like how it's specified. I guess it would be odd then also if the JS code there could not provide their own code. + +JSL: DOMException currently does not have an options bag at all. It doesn't even have “cause” right now. + +KM: I see. Okay. I mean I guess how often users create their own DOM exceptions that also feels like it would be odd to me. But I could imagine having the pieces not connecting being confusing. I do think that that particular problem is a concern for sure. I think my concern with going to stage two—if we go to stage two and AVK feels like the whole thing is invalidating code that's still something we can negotiate at stage two. I guess CDA? + +CDA: Negotiate what precisely? + +KM: If the feeling is that code would cause too much that the name code causes too much confusion with DOMExceptions code and these are different things relitigating that. + +CDA: I mean if it's just the naming that doesn't seem a huge deal to me. But I mean conditional advancement really ideally it has quite a narrow scope and it's not really you know I'm I'm just giving one opinion here. It's really a committee decision. That doesn't but that doesn't seem out of the question to me. We have some other items on the queue though. + +MM: So this is a minor point but this is in response to the comment of the code really being just a user land. I do just want to remind that I want to leave the option open for in a future JS standard for the engine itself to, for some error conditions, throw errors with specified codes. That's that's all. That doesn't affect the current proposal at all. + +JSL: Yeah. I think the door for that is definitely still open. It's very intentionally not addressed in the current spec. You know so we don't accidentally rule that out at any point. And I do want to make one other point on the DOMException. This idea that you know that the codes are two different things. Actually in practice they serve the same purpose the value space you know maybe different or how they get installed may be different. But they actually serve the same role. So calling it something else I think would actually be a problem. You know especially since you know the ecosystem's already landed on code that property code as being a very common convention. + +JHD: Yeah. I was discussing this on this issue as well and I may have missed something because I was dual wielding meetings. But I still don't see a conflict here. Like if the point of this feature is to allow you to construct an error that has a code and to create the expectation that any error might have a code an expectation which kind of already exists, DOMExceptions will already have code. And any custom error type can choose to honor the user's requested code or not. And DOMException can make the same thing. So like it's just going to be fine if you try to make a DOMException with a code the web can decide to throw or it can decide to use it or it can you know only allow it if it's the same value or that it's trying to put or whatever. And then like either way you'll be able to do a `.code` on those exceptions and get a meaningful value out. So like what's the conflict here? + +KM: I think what AVK is describing here is that the pattern for DOMExceptions is to use the name field that said I don't know the details of this because this is not my area of expertise in working with the DOM. So I mean I don't have a great answer for you probably because I just didn't have a chance to talk to AVK about it. + +JSL: Yeah. I think that that's true only for DOMException. And if you look at general practice for any other error using the name is not common at all. It's only the practice when using DOMException. + +JHD: Yeah. DOMException is the weird one there. + +KG: I agree that DOMException is weird. That said, if we think that we are going to start adding this to web platform stuff, and it sounds like JSL wants to add this to web platform stuff, then we need to have a story for what we're doing with DOMException. And I would be unhappy if the story is that we spell this “.name” on DOMException but not in other places. So I don't want this to advance past stage two, I'm okay with it going to stage two but I don't want this to advance past stage two without a coherent story for what we are doing with DOMexception. + +KM: That sounds reasonable to me. I think basically the answer is that we have to have some story of how this works with DOMException. I think I'm okay with going to stage two. But have it be understood that it's not going to go past stage two without a cohesive story. + +LVU: I would agree with everybody else. This is basically the same purpose even if it's a different value space. I think there's value in the property being flexible enough to support different ways of specifying an error code anyway. So that would support the way it's being used in DOMException. DOMException could add an options bag to sort of bridge the gap. I seem to recall there's also some weirdness around how DOMException inherits from error. Like it doesn't properly inherit from error. It's just a special case of some sort. If we end up changing the name I think “name” is fine too that DOMException uses. I'm hoping we don't have to because “code” is the existing precedent in most of the ecosystem. And I would support stage two or personally I would even support stage 2.7. I think this is sorely needed. + +CDA: I'm next on the queue. So I agree with KG on the position of stage 2 but not 2.7. I do think we need to figure these things out. I don't think we have to figure them out before stage 2. But I do think we need to figure them out before 2.7. And I think that also ideally get these questions sorted out the answers to these questions sorted out before editors and assigned reviewers are looking at it. That's not to say that they can't but you know part of the 2.7 criteria is that spec text is reviewed and I would want that churn to be taken care of ideally before reviewers are asked to look at it. + +MM: Plus one for stage two. Hate 'name'. And I think that the resolution for DOMExceptions really should be that code is code. I mean that we just use we just say that the DOMException code field is consistent enough with the code that we're proposing and if you overwrite it if we if they add a options bag to DOMExceptions which is their choice not ours then it should have a code option and if it's provided it should override the code that was built in. I do that does raise a question that I'm not that because I'm not that familiar with DOMException is the existing code property either configurable or writable? + +KG: Even worse, MM. The way that code works on DOMExceptions is that there is essentially a private field on instances with the value and there is a getter on the prototype that reads the private field from instances. It's not literally that but it's effectively that. + +MM: Okay. In that case the the my recommendation about what happens I suspend that because I'm so confused. + +JSL: And it's worth pointing out that WHATWG is still figuring out what they want to do with cause. And the spec says cause needs to be an own property where WHATWG discussion has been leaning is that cause in DOMException becomes a prototype property. So even in that case they're looking at making it different than what we've specced. So I am appreciative of those of you that feel like you know we need to figure out what to do with DOMException. But at this point I don't know what that is so my ask would be if those of you that feel we need to come up with something please open an issue with a proposal on what we do with it. So I've looked at this for a while and it's just not clear what that resolution is other than allowing DOMException to continue to be its own weird thing. I do notice that we are over time though I believe. + +CDA: We are over time. We have a little bit of capacity so actually we have a fair amount of capacity. So we can continue this topic until for a while. So we're good. + +CDA: KG you're good on the WHATWG code legacy right? + +KG: Oh. No. I do want to mention this. + +CDA: Oh. Okay. Sorry. + +KG: One of the things that makes DOMException tricky, this is mostly a reply to MM, is that WHATWG considers this property to be legacy and doesn't want to use it for new things. + +MM: Oh. + +KG: If we are encouraging it then that would require a change in WHATWG policy. + +MM: I see. I was not aware of that. Are they suggesting that there's something non-legacy that plays the same role? + +KG: Yeah. So they have a `.name` property which is a string. Basically the way it works is that there was a `.code` property which was numeric constants, which they thought was bad but they couldn't really get rid of. So they added an additional `.name` property which is string values and the `.code` property—every code that previously existed was assigned a name and all errors that have a non-zero code have a name that is equivalent to that code. But new things just have a name and have a code of 0. + +MM: So okay. So JavaScript errors do have an inherited name. They're inherited from they're a class of errors so the value of the name property that's inherited is always an error class name obviously DOM exceptions don't have a zillion well not obviously but I assume DOM exceptions don't have a zillion subclasses that the name is taxonomy to be used but it's not reflected it's in a class hierarchy. Are the values of the name properties you know identifiers like they would be if they were names of error subclasses? + +KG: Yes they are. They are like NamespaceError or, I don't know, SecurityError. + +JSL: Or AbortError. + +MM: Okay. That's interesting. + +JSL: Yeah. And unfortunately where the ecosystem has evolved it is at odds with this. When user code knows it's working with DOMException it knows to look at this. Every other error doesn't do this. And in trying to make where I create a new TypeError where you know where I change the name to something else it's actually going to break existing code because there's a lot of code out there that checks is name TypeError. To determine whether that's the type. It doesn't do an instance of check for a variety of reasons. So it's actually for us to use ".name" for this purpose of code would actually be quite breaking in the ecosystem. So it's the one thing it's kind of the one hard stop that I would have on this. + +KM: For what it's worth I mean I think the fact that the code is a prototype getter versus an owned property is probably my assumption is that it would not be a compatibility thing mostly because it's not writable anyway. So you wouldn't be able to tell that it switched from one to the other. So in theory we could maybe change that to be consistent with whatever we choose here I think you know as I said repeatedly I do think whatever we do here has to be consistent with DOM error DOMException and I think we should make sure. + +JSL: Yeah. Given the fact that we only install it as an own property when it's passed in through the options bag on the constructor and the fact that DOMException does not take an options bag right now mean you know I think sets us up that this isn't it this doesn't break web compatibility on on on the surface. If we chose to add an options bag to DOMException then that would need to be a decision made at that point similar to what they're doing with cause right now. They would have the exact same problem where "cause" doesn't currently exist but they're looking at defining it as a prototype property rather than an owned property. Which means its definitions can be different either way. So I don't think that's all to say I don't think that's a decision we have to make. I think that's a decision that WHATWG has to make in terms of how they want to evolve the DOMException type. + +KM: I mean I think that we need to work with them to come to a cohesive design. I don't want to have two completely different exception things going around on the web. I mean to the extent I don't want to make that problem worse than it already is. + +JSL: Yeah. I mean it just needs to be acknowledged that we already do. Right. That you know in fact that's what's web compatible right now is that we already do changing that might actually be might actually break things. + +JHD: Cause already exists so if they make a prototype getter aren't they introducing a problem? + +KM: I'm not talking about "cause". I'm talking about "code". I agree with you on "cause" that that would be an unfortunate choice if that's what happened. + +LVU: (on the queue) what I was talking about regarding inheritance, there is a link to WebIDL with DOMException's specialness: https://WebIDL.spec.whatwg.org/#js-DOMException-specialnessqueue here? + +JSL: So it sounds like we have I'm just going to check. It sounds like we have consensus for at least stage two? Is that correct? + +MM: I support stage 2 not 2.7 until DOMException is settled. + +CDA: I believe you had a good amount of support for stage two. Folks can feel free to re-express that support. + +DLM: supports stage two. + +CDA: I support stage two. Does anyone object to stage two? Seeing no objections. You have stage two. + +JSL: Thank you very much. And just to close it out. Again, those of you that see a need for us to reconcile with DOMException, I would love an issue opened up with some ideas on how we can do that. + +### Speaker's Summary of Key Points + +* We have spec text, test262 tests, and implementation for v8 prepared. +* Possible conflict with [DOMException](https://github.com/tc39/proposal-error-code-property/issues/2) is only unresolved discussion + +### Conclusion + +* Stage 2 +* Further advancement blocked on reconciling with DOMException +* Request for those who want DOMException alignment to propose a path forward + +## Declarations in Conditionals for Stage 2 or 2.7 + +Presenter: Devin Rousso (DRO) + +* [proposal](https://github.com/tc39/proposal-Declarations-in-Conditionals) +* Slides presented. Link TODO + +DRO: I have no presenter notes but I'll give it my best shot. + +DRO: I'm here to try to present from declarations in conditionals for stage two. Basically as a quick recap because I imagine that most of the discussion is going to be the you know time box. Basically the idea behind this proposal is, let's pretend that we have some very expensive operation on a getter for example. We have an object. We have a get for bar that does some expensive operation creating a giant array and sometimes does something else. In the current state of things if you wanted to sort of use a getter as a regular property access and you don't necessarily know any better you may try to fetch this twice. But to avoid this, if you use this multiple times, you now have to create multiple variables, and they live beyond the immediate scope of using that if. And they're more exposed, and you have to add 1 and 2 and all these other sorts of things, and it just gets more complicated. + +DRO: So my proposal was basically to allow you to create variables inside the condition of an if. This is a pretty popular thing in a lot of other languages. It's something that I've wanted to do and find myself wanting to do all the time. In addition to that, we— you know, it allows for you to declare basically as many times as you want, the variables don't clash outside of the if, and in addition to that, you can also add additional conditions. So not only scoping the variable to the conditional, but also allowing you to control the exact nature of what it is that you're trying to check, whether it be for a particular length property, whether you're declaring multiple variables and doing something specific with that, sort of like a `for` or, or even doing just destructuring inside the if as well. Note that in these second two cases, where multiple identifiers are created, the second expression is required. So only in the scenario where you're not doing any destructuring, a very basic single identifier will implicitly allow you to check it for truthiness. Whereas in all of the other scenarios where multiple identifiers are created, you must provide a second condition to actually specify what it is that you're checking. + +DRO: The big point of contention that has been for a little while now, for any of those who were at the TC39 meeting in Tokyo last fall, the big point of contention is whether or not the bindings are visible inside of the `else`. The argument for this is that it allows you to understand what exactly, so to speak, went wrong. You know, why was the check not valid? Maybe in this, for example, A was not valid, or B was not valid, or potentially both of them. It lets you sort of introspect a little bit of what went wrong. The arguments for keeping it only in the if are that it lets you reuse variables a little bit more cleanly, it preserves the lifetimes, and also makes things like using a little bit faster, in that you don't keep things alive longer than, you might potentially want to. So I would personally argue, based off of that, that no, we should not expose things in the else. That has been my preference. And that has been something that I've seen from a number of other folks, though there are some who do believe it should be in the if. And I do believe that the using example is one that particularly strongly supports the idea of keeping it as minimally scoped and minimal lifetime, as possible. There is also one thing that was pointed out from the editors, in that I have changed since November, in that now bindings are TDZ in the else, meaning that if you try to use it, you'll get a ReferenceError. Even if there is a variable outside of the if with the same name, you'll still get a ReferenceError. So you will either have to redeclare it, either using the same syntax, or inside of the else, in order to be able to use it. So with that all being said, last time, when I presented this in TC39, in Tokyo, I made the mistake of not really properly understanding what spec text meant, so now I actually have proper spec text and proper, semantics and all of those rules. So I am once again asking for stage two. And the queue is already getting big. + +KG: I will present my note just a second, but before I do, I want to make sure RBN had an opinion that he put in the delegates chat yesterday. And, he's not able to attend because of a family emergency. I'm just going to, I want to make sure that his opinion is presented. I'm not a co-delegate of his anymore. I just want to make sure we don't forget about that. + +JHD: Feel free to do so, KG, but I had told him I would do it for him. + +KG: Great, okay. Just wanted to make sure that wasn't lost. I will have my own opinions. So, I think that the `using` example that you went by quickly motivates the whole proposal on its own for me. With regular declarations, this is just like a nice little bit of syntax sugar. But actually, assuming that the `using` is disposed before the `else`, the thing that you would have to do to get those same semantics without the declaration in conditional is actually quite complicated. It is not trivial sugar. So for me, that the ability to put `using` there actually motivates the proposal. Without that, I don't feel strongly about the proposal, but, assuming that the binding is not visible in the else and the using is disposed before the else, that motivates the proposal for me. + +MF: Yeah, DRO, can you go back to the explanation for the `else`, the binding not being available in the else? Yeah, I just think these are not the most compelling reasons. I think the main reason for the binding not being visible in the else is because you can just create a block around the if, and you can put the binding there, and then this proposal would have no value on top of that. And if the binding was visible there, I would be against this proposal. So I think that the major reason for the binding not being available in the else is so that this proposal actually gives you something new. + +MF: Moving on to my next topic, the TDZ opinion. I just wanted to make it clear, you said that it was an opinion from the editor group. This was not an opinion from the editor group. I did, and probably unfortunately, when giving you feedback, gave my editorial feedback and also gave my own personal normative feedback. I thought I made the difference clear between them, but I just wanted to clarify that that opinion was my own and not coming from the group. Not that they necessarily wouldn't support it. Maybe they would. But that's not the purpose of the editor group. + +DRO: That's my bad on the mistaken understanding, then. And I apologize for attributing it as such. + +MF: And, on that topic, you know, I wanted to explain my reasoning for that. The other languages that have this feature, I think either entirely or almost entirely, all make the binding available in the else. I think somebody who is used to that behavior might try to reference the binding that was introduced in the if in the else. Why is that a problem in JavaScript? Well, the reference will just read through. And it'll read through all the way to the global object. And often, we have bindings like "name", for example, or other things that on the web are just very, very common words that it will end up actually resolving to. So it's not like there would be a ReferenceError. It's not like there would be an undefined value. It would actually just get a thing that is the wrong thing. And that is like a really big hazard that I want to avoid. So, having it TDZ there would mean like they definitely get a ReferenceError, like it tells them you should not have written this name here. + +KM: Along the same note there, I agree that if we're going to get rid of the thing, it shouldn't it makes sense that you might not want to do that. I would prefer a syntax error over TDZ error. With some caveat that you can, define a new let and it will unpoison the value in the else. The advantage of that is that, yeah, if you're coming from a language like C or C++ or whatever, any of those other languages that have it in the else, that you'll know immediately that it's not there, and something went wrong. And you need to restructure the code somehow. + +OFR: Yeah, I just want to mention a really, really strong preference for the binding being available in the else case. I think everything else is very surprising given no other language does this. And there are plenty of use cases where you actually want to be able to use the binding in the else case. So yeah, there's I mean, it's basically on this slide, but I don't think it's a particularly good example here. There are other examples where you assign something and then I don't know, test. Like, you can for example, read out the property of an object and then test its type and then do something either or, and in both cases you will want to use that name actually. So I think there are many cases where actually the binding being available in both cases is useful. And everything else is super surprising. And yeah, so I would be strongly against removing the binding from the else case. + +KG: Extremely strongly disagree. Also, you said that it makes it different from other languages. I believe you're just mistaken. As far as I'm aware, C++ is the only language where it's visible in the else, and in all of the other languages I'm aware of that support this construct, including at least Swift and Rust, it is not visible in the else. So I think we should do the normal thing, which is for it to not be visible. Also, I agree that there are cases where it's useful, but it is trivial to just put the binding in front of the, like, in the surrounding block, whereas it is not at all trivial to do the thing that is on the bottom right of the slide here with `using`. + +MF: 100% object to it being available in else, declare it in a containing block instead. + +NPU: What I wanted to say, further to what OFR said before is that, there are other languages that do one thing or the other, but I think that what distinguishes the behavior of these languages is whether we are talking about a declaration or some kind of pattern matching. Somebody, I think it was MF, before mentioned Rust and the behavior of Rust. So, in Rust, they don't have a declaration there. They have a pattern matching there. And it makes absolute sense to disallow these variables in the else case because when the pattern does not match, it's obvious that you don't have a declaration for the else case. On the other hand, languages like C++ or, in this case, the syntax that we are trying to introduce, which is, exactly the same as C++ is, this tends to, point to the direction of having a declaration there, not a pattern matching. It's, the declaration and the condition are clearly separated. So I think it would make it would make it very confusing for programmers if, the declared variables did not exist in the else case. + +CM: I find MF's point about using a containing block completely compelling. + +LVU: The way I see it, making it a I don't have a strong opinion either way about whether it should be available in else. The way I see it, making it available in else helps common cases, but not making it available in else is internally consistent with how block scoping already works with the rest of the in the rest of the language. So the main thing is that it seems to me that making it TDZ in else or even worse an error that seems to me like it's the worst of both worlds. It neither caters to common use cases that have to do additional user effort to work around it, nor is it consistent with how block scoping works in the rest of the language, or at least I cannot think of any relevant precedent. So to me, I think that violates the principle of least surprise. I can see how it helps prevent footguns, but to me, it seems that that's the job of linters. It shouldn't be part of the design of the language. + +KG: Oh, yes. So this is in regards to the point about Rust having pattern matching rather than declarations. Rust's pattern matching looks like declarations. Like, as a Rust user, I don't super strongly distinguish between the sort of pattern matching that you get in an if and a declaration. Like, you're just declaring a variable. Also, we didn't spend very much time on it, but I want to keep going back to it. The, this syntax allows you to do something which is very hard to do, which the thing that is in the bottom right, where you want to do a using declaration and you want to do one thing in the case that the, the thing is, well, I guess it's not quite the thing in the bottom right. It's the, I wish we had an example on the slides. So this proposal includes a second form, which is where you have a semicolon after the declaration, and then you have an expression. So you might say, `using file = getFile(); file.needsUpdate`. The thing that that does, assuming the binding is not available in the else is very useful and very hard to do if the binding is available in the else. And I think that that is basically for me, the main reason to have this proposal in the first place. And that just doesn't work if the binding is available in the else. I don't think that there is a strong reason to put it in the else except for symmetry with C++. And I've given that C++ basically stands alone in having the binding visible in the else. I don't think precedent from C++ should be compelling. + +KM: Yeah. I mean, I agree that I think the using is the case where it's particularly useful. In particular, yeah, it closes your resource in the case that, the resource failed to load or something else went wrong. Or sorry, you had some other piece of code in there that you want to, bail out of. And it disposes of it much sooner. I think I used to be on the form that it should be available in the else. But I think that the using case motivates it enough that it should be consistent between all of them. We should probably be consistent here. + +OFR: Yeah, I'm not entirely sure about this, about this argument with the using. I mean, especially the example that you just provided before, like, file needs updates. I don't know. Like, I might need to do something with the file also if it doesn't need an update. To me, it doesn't seem clear at all, to be honest. Like, that one or the other is useful. + +KG: Well, the point is that you can already do that just by putting the declaration outside of the if. Like, if you want to use the value in the else, you can just put the declaration outside of the if. That's completely trivial. Whereas the thing that this lets you do, in the case that you want to dispose before the else is actually quite hard to write. I put an example in the delegates chat. + +OFR: Is this a common thing that you want to do, that you would not need to do anything with the resource in the else case then? + +KG: It's pretty common. + +OFR: And also the other thing would be that you would actually benefit from the shorter lifetime. I mean, the example that you also gave there, I guess, . + +KG: Yes. This came up in Rust. Rust had basically exactly this. You acquire a lock and you do something if a condition passes and otherwise you release the lock and do something else. And, they actually used to have the semantics that the lock was released after the else. And this was a famous source of deadlocks; many, many people ran into this. And they actually changed the semantics, which they don't really like to do, but they changed the semantics of when the disposal happened, just to avoid these deadlocks. So this came up a lot. I agree that you don't use locks as much in JavaScript as you would in Rust, although that might someday change, I'm really hoping we can revisit the structs proposal someday. But, I think it does come up, yes. + +JHD: Yeah, so I'm here representing RBN's position, since he's unable to make it today. Or this week. So, essentially, in November, he asked that we postpone this proposal's advancement until we discuss cross-cutting concerns with pattern matching and possibly extractors as well. And he doesn't believe that was ever addressed. If declarations and conditionals advances to stage 2 or 2.7, it will likely force pattern matching into a direction that many of the champions have expressed opposition to. It may mean yeah, there's some more details he's got in the pattern matching champions, matrix channel, but essentially, he wants to block stage 2 as well as 2.7 because 2 means a chosen solution, and he's not yet convinced this is the correct solution. And wants those cross-cutting concerns discussed more in depth. + +KM: I don't see how this would impact like, a lot of decisions here, especially in the else case, would presumably equally apply to pattern matching. + +JHD: Yeah, that's not—the else case isn't the issue. It's the grammar of doing the pattern match itself, which in the past feedback from delegates has moved the pattern matching syntax to use declaration syntax for consistency. And so this is basically overlapping the syntax space that the pattern matching proposal has that previous feedback has moved the pattern matching proposal into. + +KM: What pattern matching I guess I don't know the exact syntax we're talking about here, but wouldn't that same problem exist for loops and the syntax already exists in for loops? So like, you'd still have the same problem you'd have to solve? I think. But anyway, all other people go. + +JHD: I don't think pattern matching has any intention of doing a pattern match in a for loop header. + +KM: You wouldn't want to, it feels like you might want to do that, but okay. Anyway, I'll let you go. + +JHD: You might want to have a pattern match expression be the terminating condition, but I don't think there's any conflict there. + +DRO: Yeah, I mean, I talked with RBN about this on matrix and also there was an issue opened on this proposal talking about the overlap. And I sort of agree with Keith, and I believe there was one or two other folks who mentioned that, I don't see where there is a syntax overlap. And sorry, there is an overlap, and I also feel like there is a pretty big syntax difference between the two proposals. And if there's different syntax, there can be slightly different behaviors. I mean, if that is really that big of a problem, certainly I'm happy to talk with him. But I just don't see where there is an overlap. And, I'm not entirely sure what to do in the same like, the proposal that I have is a very specific thing, and I'm not entirely sure how like, I don't really want to bring parts of pattern matching into this proposal because I feel like that changes the nature of this proposal. And I also feel like. + +JHD: Yeah, I don't think that's a problem. + +DRO: Well, I'm okay. That's not the sense that I got. But I would also say that if, and I apologize if it sounds like I'm cherry-picking your words because that's not my intention, but the phrasing and the understanding that I sort of the feeling that I got from talking to RBN was that my this proposal would sort of set a precedent that would force the pattern matching proposal to go into a direction that the pattern matching champions didn't like. And some part of me finds concern with the fact that this proposal has been sort of guided in a direction based off of feedback from delegates. So if that direction is not the direction that the pattern matching champions like, then the pattern matching champions are at odds with what the delegates are indicating. And I'm not really sure how to reconcile that. + +JHD: The pattern matching direction that we're that we don't want to go in is something that surprise blocked our stage 2 advancement for pattern matching a couple of years ago. So it's previous delegate feedback. We cannot go in that direction. Our preference was the original direction, and we got delegate feedback to move away from that. And we would have to, like, I hear what you're saying, that delegate feedback has guided this proposal. But if this proposal cuts off the pattern matching direction that delegates will tolerate, then there's no other direction for pattern matching to go. + +DRO: Sure, but by that same token, if this proposal has to change course to not conflict with pattern matching and in doing so conflicts with what delegates have said that this proposal should do. + +JHD: Yeah, then that. + +DRO: How exactly do you make that work? + +JHD: And I agree. That's yeah, that's I mean, I think that's the discussion Ron would like to have at length. I can't you know, I can't speak fully for his position. I can only represent it. The best to the best of my ability. But, yeah. He has a lot more details in his head about the syntax and the overlap and the direction and stuff. So I think, it's worth at least hopping on a call, I think, with you and he and figuring out what like, getting on the same page about where the conflicts are, or that there are not any. And, and see if a solution can be hammered out. + +USA: We're running closer to the end of the time box, but not quite yet. Anyhow, MF, is next? + +MF: Yeah, so I am sympathetic to wanting to make sure we get alignment between this proposal and pattern matching. I have no dog in this fight about whether it's actually appropriate or not, but I do think it's appropriate to give both champion groups an opportunity to try to get alignment here or at least time to do so. I think that a reasonable amount of time is just one meeting. And any further delay would not be appropriate. So I encourage these groups to work with each other before the next meeting because I would not be so willing to delay any further if this is brought up again. + +MM: Yeah, so pretty much agree with MF. I don't want RBN's objection to lead to an infinite delay. I'd really like to get this resolved by next meeting. But the reason why I find RBN's objection compelling might be different than RBN's reasons or the pattern matching folks' reasons. With regards to the procedural model, before I state my reasons, let me just state, I am a delegate, of course, and I'm a delegate that has not been involved with pattern matching. I'm actually not a fan of the pattern matching proposals I've seen, but I am very much a fan of extractors, which need some pattern matching framework around it. The reason why I think it should block advancement of this to stage 2 until we resolve is that, if we could say that it's just pattern matching in the head, that is the extension, and not declarations in the head, then we're switching on, patterns are refutable. Whereas declarations are currently irrefutable. And the, switching in the if in the if with the variables not being available in the else, which I agree I don't want the variables available in the else, just is much more natural if the thing that's in the head of the if is refutable and we're in the `else` if it gets refuted and we're in the then part if it does not get refuted. And then obviously you can you can you know, your pattern matching syntax can include a, you know, something around a Boolean expression if you really want to turn it back into doing a test on Boolean. So I want to not approve stage 2 currently on that basis, but I really would like to get this resolved. That's all. + +USA: Thanks, Mark. We have a reply by Keith. + +KM: Yeah, I mean, I guess, I'm concerned about stalling this I mean, one meeting might be fine, but I'm concerned about stalling it generally because I mean, pattern matching has been a proposal for, I think, roughly 10 years. It's still stage 1. I think I have I think there's a lot of concerns on the committee generally about pattern matching. I didn't think about extractors. I know talked about extractors as possibly related, but the it does feel like I'm not totally convinced that pattern matching will ever end up being a complete feature. I mean, I think there's too many conflicting interests. And I may be I'm wrong. I'm not saying that I, you know. I don't know. But it feels like there's a lot of competing interests in pattern matching and how it should work. And the it would be a shame to completely block something like this that I think has real use in particular for the using case, as we talked about. On forever. On a proposal that may or may not happen. + +DLM: Sure. I'll be quick. I just want to express that I agree with Keith on this one. I really like this proposal, and I think it's solving a, fairly obvious and immediate need. And I would not like to see it stalled indefinitely based upon pattern matching because I share the concern that that might not ever make it into the language. + +PKA: I just wanted to ask, is it possible nobody needs to answer, but just raise the question, is it possible to do `using` with pattern matching, at least as currently constructed, because I agree with Kevin that using is, kind of the killer app for this feature, and that, that should be the main motivator here. + +MM: I just wanted to ask about all of the other control structures, whether it makes sense to extend this proposal to them. I listed `while`, `try`, and `switch`. I think it does not make sense for `try`, but wanted to get your opinion. While, it could make sense of, but the counterargument could be, well, it's so similar to what four or, the four semicolon form already does. And `switch`, I often find myself declaring variable right before a switch, that I then switch on so that the variable is available inside the body of the switch. And for that, since there is no else, default is not else, the, having just a block around the whole switch as in the Michael's answer for why not else, seems like a good counterargument, but like I said, I want to get your opinion on, extending this to other control structures. + +MM: And I'll also just, while I've got the microphone, mention the other thing, which is, TDZ implies that the error doesn't happen unless the `else` is executed. Whereas SyntaxError, like KM suggested, means that you get a static rejection. This seems like there's no reason for this to be a dynamic issue. The error should be early, not late. That's all. + +USA: Quick point of order. Less than five minutes remain, and it is a long queue. But we can get through it. First, we have MF, who says, while support is already in the spec text, then we have a reply by DRO. + +DRO: Yeah. I apologize. I could have given more in these slides. I was trying to keep it as brief as possible because I imagine there would be a long discussion, as there has been. So yes, it is supported for `while`. And as you point out, there is no else. It's not in `try` because, I'm no pun intended, *trying* not to do a huge amount of syntax or behavioral or whatnot changes. And it's not in switch primarily because many delegates expressed a strong dislike for it the last time this was presented in Tokyo. And I feel like the semantics of that in that scenario are a lot more weird than something like a while or an if. I don't know that I've ever needed that kind of production. Whereas I probably daily wish that I could put a declaration in an if or a while. At least an if, while is very frequent as well, almost daily. + +MM: Okay. So I'm happy with that answer. + +JHD: Yeah, I don't want any improvements to switch statements. They're terrible and need to die. That was my comment. + +MM: With the TDZ thing, I just want to ask, is does anybody favor a dynamic error that happens only if the else is executed as opposed to a static error? Is there any reason to not have a static error? + +EAO: I mean, having a time-traveling TDZ would be kind of funny. But that's the only reason I can think of. + +MM: What is a time-traveling TDZ? I think I don't understand the time-traveling TDZ, but it sounds like I don't need to. + +EAO: You don't need to. You don't want this. I don't want this. + +DRO: im fine switching to static/early error (EOM) + +MM: Okay. So in the, in the written responses, I'm seeing no support for, for TDZ for the runtime. so the first reply from Devin has an EOM on it, so we can go past that. Waldemar, yeah. + +WH: I think it should just be a syntax error. + +KG: This would be the only place in the language where we do this kind of syntax error for a reference to a binding. Like, just because the binding is unbound or otherwise unavailable. I guess I'm okay with it. I don't see anything wrong with it. But it is kind of weird. + +MM: Okay. So I take that not to be an objection. + +MF: static error is fine by me (EOM) + +KM: TDZ has runtime cost too (EOM) + +MM: and then Michael, about reference error. That's an interesting point. Go ahead, MF. + +MF: Just WH mentioned a syntax error. I had assumed that we would make it an early reference error and it's not really important, most people don't distinguish between them, but that seems like the right thing to do for me. + +MM: Wooow + +MM: Okay. I do not object. It's we don't have any cases right now of an early reference error. Interesting. Okay. + +MF: I'm not 100% sure if that's correct. I need to look. + +NRO: Aren't all early errors syntax errors? + +KM: Whether it's a reference error or syntax error is something that could be solved at stage two. That seems like it would be not a super significant semantic change. Assuming that we're all agreement that it should be an early error. + +MM: Yeah. I would agree with that. So Nicholas, did you have more to say on that? + +NRO: We can figure it out offline. + +KG: LVU spoke earlier, in defense of not making this an error, I don't know if she's on the call. + +JRL: I mean, I'll take that because that's my topic exactly. Behind all of these current replies, the one I've had is, "Why is this an error at all?" Can you go back to the slide before this where you showed the TDZ or what we called the TDZ, but it's now probably a reference error? So if we like, the reason I dislike this proposal is because we don't want it in the else condition. And the reason I dislike that is because it means the else condition, which is a child of the if condition, cannot see the binding in the if. Okay. Let's assume we have to have the dispose happen in the if condition. Why can't we the else refer to the outer data here? Make the if only contain the lexical binding in the else is outside of that lexical binding. + +DRO: I mean, personally, I would say that I tend to agree with LVU that this would be something that codebases could decide for themselves for linter rules as to whether or not this is something that they would allow. I mean, it definitely seems like a bit of a footgun, but I also could see a scenario where a developer might actually want to do this. And ultimately, it seems to me like this is something that could be decided as a preference of whether or not this is something that is disallowed by linting. Or it's something that is allowed and usable. I mistakenly thought that this was editor feedback as opposed to an opinion. And so that was my blunder in terms of just sort of blindly going ahead with this. I apologize. This is the first proposal I've ever worked on, so I'm still learning the process. + +JRL: All right. So we have a point of order. We're now two minutes over. We're getting into our lunch break. Here we have technical difficulties with CDA trying to do the sharing. So I'm taking over. We're two minutes over. We have a bunch of replies that we've kind of gone through, so I'm just going to mark all those as being responded to. We've gotten to my topic, and then OFR is next: "in Rust, if-else is an expression". We do not have time to go through this further. If you can talk about this over lunch or on the GitHub repo, that would be preferable. Barring any other point of orders, I think we need to call it now for lunch, and we will be back in 56 minutes. All right. See everyone then. + +### Speaker's Summary of Key Points + +* Proposal now has actual proper spec text. +* Bindings are not exposed to the `else`. + +### Conclusion + +* Need to meet with champion of Pattern Matching proposal. +* Decide whether to make referencing bindings in the `else` an early error or allow it. + +## Async iterator helpers open questions + +Presenter: Kevin Gibbons (KG) + +* [proposal](https://github.com/tc39/proposal-async-iterator-helpers) +* [slides](https://docs.google.com/presentation/d/17_mL3guxupbKWKr1EbDdML9K9kPTqElbf9xgoZZDBh0) + +KG: Okay. I am hopefully sharing my entire window and not just the presentation for reasons we'll get to in a moment. But hello, everyone. Welcome back to Concurrent async iterators for the 7th or 8th time. I have presented this. I have finally actually made some progress. Which we'll be getting to in a second. I am not at this time asking for advancement, but I have a handful of open questions to come to the committee with in case people have opinions on them, which I think once resolved are the only remaining open questions such that I can then write down the spec text and come back for a stage 2.7. So brief recap. What are we talking about? Oh, sorry. We'll get to this slide in a second. Brief recap. What are we talking about? We are talking about dot map, dot filter, dot flat map, on async `iterator.prototype`. They don't currently exist because we realized in the process of specifying them that actually they can support concurrency in this kind of magical way where the user can just do multiple pulls from the resulting iterator and have that do things in parallel. Or sorry, do things concurrently. And in this example, we would potentially do two potentially be doing two fetches concurrently or a fetch concurrently with a pull from the underlying iterator. There is also a helper that I think is worth including although it doesn't necessarily need to go in the MVP called dot buffered, which like the name suggests, just buffers results internally. So it will do these pulls it will do two pulls concurrently so that if you are iterating over it sequentially, you still get potentially some degree of concurrency. I think this is a useful helper to have in the proposal because it allows you to get this concurrency without manually driving anything at any point. + +KG: So handful of design principles that I've been trying to follow. And again, we'll get to implications of these in a second. It should be order-preserving. This actually gives up a lot of concurrency. But is unavoidable. It is impossible to avoid giving up concurrency. If you want to be order-preserving, and not have unbounded buffers, and I do think that the basic async iterator prototype map and filter and flat map need to be order-preserving. Another principle is that the helper owns its underlying iterator where ownership means that the helper assumes that it is the only one pulling from it and more crucially, closes the underlying iterator at the appropriate time. So that's if the helper is closed or the helper throws an exception itself that doesn't come from the underlying iterator, then the underlying iterator would get closed. Also, we want to avoid losing values. This, I think, is a little bit subtle, but you can have this weird case where you do, say, two pulls from a map. Sorry, two pulls from an underlying iterator that you are mapping over, and then the mapper function throws for the first value. Well, the second value is sort of hanging out in limbo now. So I am trying to ensure that it is still possible to recover that value or rather, you pass that value to the mapper function and then recover the result of that. And finally, we do need to have some limits on the sorts of iterators that we are able to handle. And this is the primary one. Which is that we assume that an underlying iterator will never give us a done false which is sequentially following not temporarily, but sequentially following a done true in the sequence of values. The question of how do you handle this incoherence is open and again, we'll get to that. + +KG: Okay. So that's the background. Before we get into the open questions, I have [a live demo](https://bakkot.github.io/async-iterator-helpers-implementation/). I am very excited about this demo. I spent a slightly unreasonable amount of time on this. But this is showing this demo is actually hooked up to an actual implementation of my current best choice of semantics for map, filter, and flat map. There's an interactive thing that you can do. But I do want to spend at least a few minutes playing around with some of the scenarios that I have in this demo because I think that they are important to understand what we're getting into. So I guess, yeah, I'm going to spend the next at least 5 or 10 minutes just going through well, maybe not 10 minutes, but a few minutes at least just going through the demo. + +KG: So pause here if anyone has things you want to bring up before I do that. Nothing in the queue, though, so I will just proceed. + +KG: Okay. So map. How does map work? So this is actually more of a how does this demo work? You see there's this dot which has appeared on the screen. Hopefully, you can see. That dot is where the next external to the machinery thing is going to happen. So a dot next to the result iterator means someone is pulling from the result iterator. So someone does a pull and now they have this unsettled Promise which is what this box indicates. The next thing that happens just immediately as a consequence of the machinery, this will do a pull from the underlying iterator. All right. Let's suppose we are not trying to do any concurrency here. So the user isn't pulling multiple times. They're just going to wait for their result to settle. So the next thing that happens is the underlying iterator will at some point give us a value. So dot indicates something external to the machinery is happening specifically what's happening is that this Promise is settling. Okay. So it settles. It goes into some internal buffer which is calling the mapper function. At some point later, that mapper function which can be asynchronous settles. And sorry, the Promise from that mapper function settles. And we get a value. And that value immediately goes into the result. All right. This is just how map works. There is nothing interesting here. Slightly more interesting example concurrency. So in this case, we do a pull. And again, it pulls from the underlying iterator. But now, the next external to the machinery thing that happens is that we do another pull. Sorry. No, I guess in this example, the next thing that happens is the underlying settles. And then while we are waiting for the mapper to run, the next thing that happens is we do another pull. So here, we do another pull from the underlying. And so now you can see we have two things happening concurrently. There is the mapper function and the pull from the underlying. So great. We got concurrency. Supposing that the underlying settles, now we might have two mapper functions running concurrently. And it is possible for those to settle out of order in the sense that we could have the sequentially second one settle before the sequentially first one. And in that case, for map specifically, that allows us to deliver a value to the result Promise. We can't do that for filter or for flat map. And we'll get into why in a second. But for map, we can. One of my open questions is, should we? And we'll get back to that when we get back to the slides. Anyway, let's just play this out. The only thing that can happen after this is the internal Promise for the mapper function can settle and go into our result and we're done. So great. That wasn't too complicated. + +KG: I am going to go do the equivalent demos for filter and flat map just so we have an idea of the basic semantics for all three of these helpers. So filter. Running through the non-concurrent example much faster. Do a pull from the result. Pulls from the underlying. Underlying settles. Now we are invoking a predicate on that. Again, the predicate function can be asynchronous. It doesn't have to be. And there's an open question around that. But it could be asynchronous. And the result is, let's say, true. Okay. We just get our value in the result. Then we do another pull. Same thing happens. But let's say in this case, the predicate returns false. Then immediately, this will sort of throw away that value because this is a filter. And we will issue another pull from the underlying iterator. Which let's suppose that returns true. Then that can go into the result. Again, nothing particularly interesting here. This is how filter has to work. There's no nothing about this would be different in a world where we didn't care about concurrency. But let's make it a little more complicated and do a little bit of concurrency. So we are now going to issue two pulls from the result iterator. Which will cause two pulls on the underlying iterator because we know that we are going to need at least two values. And those can settle in whatever order. We might suppose that the second one settles first. And let's say it settles with false. Then we'll immediately do another pull on the underlying because we still know that we're going to need at least two more values from the underlying. So we do another pull. Let's say that settles immediately. And we invoke our predicate. And that settles immediately. And settles with true. So now we are in an interesting state with map. We when we got this later sequentially later value, we could just put it into the result Promise immediately. With filter, we can't do that. So this just has to hang out in this internal buffer. And the reason why is, well, let's suppose that our predicate well, yeah. Let's suppose that this predicate returns true. Then the value will go into the second slot. But if it returns sorry, then the value from the second buffer slot will go into the second result slot. But if it had returned false, which I think is this demo, yes. And this is same scenario as previously, but now the predicate returns false. Then this value would have gone into the first result slot which is a necessary consequence of our intention to preserve ordering. And we do another pull from the underlying just so we can fill our outstanding value. And that might go there. So this filter is different from map in that we cannot ever eagerly fulfill a value in filter. With one exception, which is if we get a done true from the underlying iterator, which I've indicated with this done true and this cross-hatch, which I don't know if that's coming across on the slides. But from the underlying iterator, we can get a done true at any point. And now, because we are assuming that we're never going to get a done false after a done true, we know that there are at most two values that we could ever get out of the sorry. There is at most one value that we could ever get out of the result iterator because we know that at least one of the values from the underlying iterator did not pass the predicate. And that there are at most two values from the underlying iterator total, which means there is at most one value from the underlying iterator that passes the predicate, which means we can settle the second Promise from the result with done true immediately. And I have with these little tombstones marked those as now being closed, which is only relevant when we get into calling dot return, which we'll get to later. But we could still get that value from the earlier pull. And it could by hypothesis pass the predicate and result in true. So there's nothing I think there's nothing surprising here. Hopefully. But it's just an illustration of some of the bookkeeping that we have to do and some of this oddity where you can have a done true specifically settles before a done false. And let's actually replay that scenario, except let's suppose that when we get our value from the first pull of the underlying iterator, our predicate returns false. At that point, we know that there are no values from the underlying iterator, which pass the predicate, which means that we settle the first Promise with done true. So we have this kind of odd situation where we are marching back from the end of the result iterator and resolving things with done true as things fail to pass the predicate. Again, I think hopefully not surprising. This is really, I think, the only way it could possibly work. + +KG: Okay. Let's do `flatMap`. The big complicated one. Flat map is fun and difficult to represent visually because we have several iterators in play. So we'll see how that's going to look. But again, let's start with no concurrency. So we just do one pull from the result. That does one pull from the underlying. That resolves the value. And then we invoke our flat map function on it. Which is potentially asynchronous. But at some point, resolves with a async iterator itself. So now we have even more boxes on the screen and we do because we need a value. We immediately do a pull from that. And at some point, that value will settle. And that value just goes in the result right away. So nothing particularly fancy. And if we do another pull, because we haven't opened we haven't opened iterator to pull from that is not the underlying iterator, we just do a pull from that. And if we get a value, then it goes into the result. We might at some point do yet another one. And then if that one gives us a done true from the inner iterator, then we know that we have exhausted the inner iterator and we need to pull from the underlying one again. And call our mapper again. And get yet another async iterator and pull from that. Which finally gives us our value that goes into the third slot. And we keep pulling maybe the second iterator only had one value. So we do another pull from the underlying. And that one maybe gives us done true. So now, finally, we know that we're actually out of values. That was all of the values we had. Again, no concurrency at all involved in this. This is just like how flat map has to work, I think. Gets a little more complicated with concurrency. Let's suppose that we did two pulls while we were still waiting for that first pull from the underlying one to settle. With filter and with map, we would have done two pulls from the underlying iterator because both filter and map, we know that we need at least one pull from the underlying per value in the result. Not so for flat map. In this case, we might get two values out of the first pull from the underlying iterator. So we do not do a second pull from the underlying iterator at this point. We just sort of keep a count of, well, we're going to need to do two pulls from the first inner result. So let's proceed until we actually get that one. So now we haven't inner async iterator. And we immediately issue two pulls on it. So the quirk of this implementation is that we are never doing any concurrency for the underlying iterator. Concurrency only happens on results of the flat map function. But I think that's really the only sensible behavior. Anyway, let's just play it out. Those values can resolve. Maybe we do another pull. We get done true. We do another pull. I think nothing terribly surprising here. It's just a lot more complicated. Okay. That is the basic behavior for all of map, filter, and flat map. The basic way concurrency works. Flat map has a dozen edge cases. I want to go through another one really quick because we already talked about the difference between map and filter in terms of where when we can do delivery. Flat map is more like filter than map in this regard, which is that we can't generally speaking do delivery as values come in. In this example, we pulled from the internal, but one sorry, we've pulled from the result three times. That ultimately leads us pulling from the first inner iterator three times. And those can settle out of order. We might get a done true, for example, and this tells us that there are only two values from the first inner iterator. So we do need to pull from the underlying again because we know we will need at least one more inner iterator. And maybe that one is faster. And it gives us a value right away. So now we are in this state where we have a value. We have this B1. It is going to end up in one of those three slots in the result. But we don't yet know which one. Because it is always possible that one of those earlier two Promises from the first iterator might settle with done true. And I believe yes. No, I guess I didn't have that in this example. But we can deliver results from the first inner iterator as they come in. But we can't deliver the result from the second until we know the length of the first one, which we don't know until it actually finish settling. And I guess this was the scenario where that happens. So let's play it out. Exactly the same as before. We have our value from the second inner iterator. And but in this case, the inner the first inner iterator only ends up being of length one. So in this case, the B1 ends up in the second rather than the third slot of the result. And so we might need to do another pull from the second inner iterator. So again, lots of bookkeeping. + +KG: Okay. I want to get to my open questions. I do want to show one more thing that we have to think or I guess two more things that we have to think about. Before we get to the open questions, I would love to go through all of these scenarios I spent a lot of time on them. We just can't possibly go through all of them. But this demo is online and I would really like people to play with it. But one more thing that we have to think about. Which is that iterators don't just have next. They have a return. So let's play out a scenario where we're doing some basic concurrency in map like we were before. And maybe we're getting some values. But suddenly, something else happens. Suddenly, while we are waiting for our previous Promises to settle, the user calls dot return on the result. All right. The thing that I have chosen to implement is that this will immediately call dot return on the underlying iterator. We could choose to instead queue up that result. And wait until the underlying iterator has no more outstanding Promises. I don't think we should do that. The underlying iterator can, of course, implement this queueing itself if it wants to. But the calls to dot return are themselves Promises and can be happening concurrently with calls to dot next. That said, once we have called dot return, even if the underlying hasn't settled yet, we are considering the results to be closed. So we have this tombstone here. Which means that any subsequent pulls from the result will just immediately settle with done true rather than issuing additional pulls to the underlying iterator. Okay. Yes. I'm not going to play this out. But I just wanted to illustrate that you do, in fact, have to think about calls to dot return as well as calls to dot next. And the last thing that you have to think about is errors, which I'm indicating with these boxes. An error from the underlying iterator in map can just be sent immediately to the corresponding slot in the result. Things get more complicated in that scenarios. But yeah, you do also have to think about errors. One thing about errors is that errors from the underlying iterator do not cause us to close the underlying iterator. It is assumed that the underlying iterator knows that once an error occurs, it is considered to be closed. That is not so if the error arises from the mapper function because then it is that error wasn't in the underlying iterator. So in this case, we do need to call underlying dot return. And because underlying dot return is a Promise, and we don't want to call this Promise but then just never wait for it, I think it's important that we always wait for our Promises. So in this case, we can't actually resolve deliver the error right away because we are still doing this other asynchronous operation. You might think of it as an await inside of a finally. Where the finally is triggered by a throw in the try. We need to actually wait for that to happen before the error is delivered. So only once the underlying return call settles can we deliver the error from the mapper. So I think these are all pretty much how everything has to work. I just want to talk about some of that complexity. Okay. And I think that's all of the underlying mechanisms I wanted to talk about. Last thing before we move back from the demo is if you have a question about how something works, I have an interactive demo where these buttons are wired up to an actual implementation of map doing lots of Promise with resolvers stuff. And you can just be like, well, what happens if I call next four times and then the third one gives us done true? Well, there's a question of what happens to this value? Can we get values out of order? That is, in fact, one of my open questions. So we'll be getting to that later. But if you just want to play around with what I think the semantics ought to be, there is and your question is not covered by the slides or one of these scenarios. You can play with the interactive demo. I spent kind of an unreasonable amount of time making this. But it turns out to be very hard to think through these scenarios for me without something that I can actually stare at. + +KG: Okay. Let us get to the queue before I get to open questions. + +MM: Yeah. So the early binding that map that only map can do, the given that there might be an earlier done true that you haven't observed yet, I don't see how you can bind a later thing. That might come after done true. + +KG: Ah. This is why we are saying we consider that scenario to be incoherent. So on one of my slides, one of my assumptions was that you don't do that. + +MM: So the who doesn't do what? + +KG: One of my assumptions all right. Sorry. My let me think how to phrase this. I have designed this such that I think we get the best results that are available assuming that no underlying iterator ever produces a done false prior to a done sorry. Subsequent to a done true. + +MM: Okay. + +KG: So the question of what to do in the case that the underlying iterator does do that is open. But all of my design decisions about what should happen in other cases assume that that does not happen. + +MM: Okay. So I don't think there's an independence there. I think that if the resolution to the open question is that the map results stops when the underlying gives a done true, then you can't bind the map results that come after the underlying gave a done true. And that would be fine with me because that would be consistent with the other (flatMap and the filter). + +KG: Maybe I'm not understanding. So let's use our interactive demo. So let's say we get three pulls. And you are saying we get a done true, followed by a done false. + +MM: Well, not temporarily followed, sequence followed. But we got C after I'm sorry. We got C temporarily earlier. Right. + +KG: Okay. Yes. And then let's say the mapper settled as well. And you are saying that because of this possibility, you do not want to have `fC` here. + +MM: Correct. + +KG: Okay. That is an option. I think that that's kind of unfortunate because it means that it delays this delivery. Only because we are trying to be defensive against a scenario we consider incoherent. + +MM: I agree. But the delay is consistent with the delay that you have anyway for `filter` and `flatMap`. So it just means that map is no longer a special case. + +KG: Okay. That's a valid answer. And yeah, that was one of my open questions. + +MM: Okay. That's good. + +KG: Continue going through the queue. + +MM: Yeah. So I like the buffered operation. I think that this proposal should include that because having to pull manually just really takes you out of the flow of the sense of the proposal. I'm wondering if there's some place to hang an option to say, "Just give me the results in the order in which they became ready." i.e., the temporal order rather than the sequence order. So as to get things earlier and optimize the concurrency. That I do understand that conflicts with what I just advised with regard to `done: true`. And I'm wondering if a variation of buffered is the place to hang that. + +KG: Yes. Excellent question. In fact, an earlier version of this proposal did include a `bufferUnordered`. Which, if I understand correctly, is exactly what you're proposing. + +MM: Yes. + +KG: I took it out, partially, in response to feedback from MF, who really didn't like that further option further operations on the chain would reintroduce the ordering. So Michael's preference is that we have some way of indicating that we don't care about ordering for any operation because presumably, if we don't care about it at one point, we don't care about it. If I do a dot map on the result of dot buffered, I presumably don't care about the ordering for `.map`. But the call to `.map` has no idea that the underlying thing is actually producing values in no particular order. So Michael's preference was to have a different set of helpers. And he, in fact, has an entire proposal for this, which is unordered, async iterator helpers. Where you have some way of. + +MM: Oh my God. + +KG: Switching the entire chain here. Maybe I can pull that up. + +MM: Oh my God. + +KG: Yeah. He has a proposal for switching the entire chain so that you get unordered stuff. + +MM: Okay. Interesting. + +KG: Yeah. + +MM: And then my final question is, I understand that AsyncContext is still fairly early. But assuming that both of these advance what are your thoughts about whether the context of async context follows the pull chain? + +KG: Going to be honest, I haven't thought about async context at all. + +MM: Okay. That's it for me. + +KG: Yeah. But NRO has opinions. + +NRO: Yeah. So assuming that that follows the pull chain, you mean it propagates the context from the `.next` callback into the previous callbacks. That is how sync iterator helpers work, just because they do nothing special with `AsyncContext`. So the context that all the callbacks use is the one that was active when you called `.next`. So I would expect it to behave the same way here. + +MM: Okay. I love that. The consistency with sync iteration seems like the right anchor for answering this. Thanks. + +RGN: Yeah. The slide covering your design principles I very much appreciate. Even in this discussion, you’re framing what to do for `map` as a question of consistency, rather than as a question of defensiveness. And I'd like to praise you for doing this and encourage others to do so as well when we get into similar scenarios. As a side point, I agree with all of these principles and think we should just take them as stipulated for the remainder of the discussion. + +AKI: Everyone thinks this demo is super cool! + +WH: I disagree with MM about trying to defend against the incoherent iterator scenario. What I observe here is that, even if the underlying iterator is incoherent, this is trying to make the result iterator be coherent. I don't think that's particularly useful to do. I would observe that if, instead of mapping, you're directly calling the underlying incoherent iterator, then you’ll have to deal with an incoherent iterator anyway. So trying to make it coherent does not make sense. + +MF: 👍 (EOM) + +WH: The other question I have is: what does buffered do? Is it a sliding buffer or is it a buffer that picks disjoint pairs? + +KG: Sliding. + +WH: Okay. + +KG: Yeah. So iterating over the buffered thing gives you the same values in the same order as iterating over the non-buffered thing. It's just that some of the values are being produced temporally before you would normally start producing them. + +WH: Okay. + +KG: But the sequence is the same. + +OFR: I want to second that notion that design principle slide is great to have. Can you pull it up again? Because I think I have a question. + +KG: Yes. + +OFR: Am I correct that there is a contention between the third and the fourth point here, basically? Basically, never lose value would basically imply that we cannot ignore the done true? That we cannot throw away the value after the done falls, right? + +KG: Yes-ish. I guess it depends on whether the third point extends to incoherent underlying iterators or not. + +OFR: Okay. So maybe we should have a preference to which one of these two is more important. Maybe that would be an interesting way to look at it. + +KG: Yeah. Personally, I think I am not super worried about trying to achieve anything except non-deadlocking for people who give me an underlying iterator that is incoherent. + +OFR: Yeah. I'm not even sure if I have a preference. I was just trying to figure out if there actually is a tension here or not. + +KG: Okay. And then MM is next on the queue. And I believe I have a sample which illustrates this. Sorry, MM, do you want to read your question? + +MM: Yeah. So do you so done true is not the only way to terminate an iterator. There's also the error case. Do you consider error to in a coherent iterator to terminate the iterator? In which case, do you consider a `done: false` after an error to constitute an incoherent iterator? + +KG: That is an excellent question. And this is a case where the answer is forced by this third principle. So let's consider the case where we do three pulls from `map`. The second one resolves. And then for the second one the mapper, not the underlying iterator but the mapper, throws an exception. So now we close the underlying iterator because the mapper threw an exception. That error doesn't necessarily need to go right here. But regardless of where this error ultimately is going to go, we have done three pulls from the underlying iterator. And the third pull from the underlying iterator can give us a value. Because again, the underlying iterator hasn't seen an error. And the mapper function when invoked on that value might well not give us an error. So we are left with this value, this `fC`. And in order to maintain the principle that we do not lose values, the only thing in my opinion that we could reasonably do with this `fC` is put it in the third spot. Now, it doesn't necessarily need to happen right away, but I think this is the only place we could reasonably put it. We certainly couldn't put it in the second slot. So I think the second slot has to be where we put that error. And this means that I think this is the only sequence that we could reasonably get from this scenario. And I think that this scenario is a reasonable thing to happen. Mapper function throwing an exception, totally normal thing to happen. And the underlying iterator was fully well-behaved. Which means I think we have to consider this to be coherent. So the answer to your question is no. I do not think that getting a `done: false` after an error is something we consider incoherent. I think that we should handle those cases reasonably. + +MM: ..Okay. A `async for of` loop will stop at the will terminate at the error. And it will never see the `fC`, correct? + +KG: Yes. That is absolutely true. So I guess I should elaborate on my "don't lose values". That's more about if you are driving the iterator yourself and you are being really careful with where those Promises end up, then you don't lose values. But yes, of course, if you just iterate with a for-of over this, you will never see the `fC`. I consider that fine. I mean, it's the same sense in which if you do a for-of over `buffered`, and then early return from the first iteration of the loop, you'll never see that second buffered value. There are lots of ways to consume a thing where you are going to end up ignoring some of the Promises that you previously produced. I don't think that's a problem. I'm trying to maintain the principle that if you want to be careful to ensure that all of the values are handled, you can do so. + +MM: Okay. I'll just register that I'm disturbed by this answer, but it still might be the best among only bad choices. + +KG: Yeah. I also want to say that whether we consider something incoherent, in these design principles, really only affects whether we are trying to be delivering good semantics in those scenarios. It's not like we can't ignore those cases, whether or not we consider them incoherent. So I mean. Even if we did choose to consider this incoherent, I don't think that would necessarily affect much. It is my opinion that because I think that we basically have to produce this scenario in this case where we didn't have an incoherent underlying iterator, because we produce this scenario ourselves, we should not consider it incoherent. + +RGN: Good. That's actually exactly the question I had. So this was the case where the error came from the callback provided from outside. It's also possible that the `next` itself throws. + +KG: Right. So my point is that from the consumer of the result, the result is the same kind of thing as the underlying iterator. So for the consumer of the result, you get a next from the underlying that throws. Which means that we produce things where the next throws. What produces a projector Promise. + +RGN: So question to MM, does that match up with what you were asking about? + +MM: I'm just going to stick with I am uncomfortable with this answer, but I might be more uncomfortable with any other answer. + +KG: Okay. Okay. Thank you for indulging me in this demo. Let's get to the part of this I want to get to, which is open questions. I have a bunch of these. They're not super related to each other. I am going to try to get through them pretty quick. But some of them are complicated, so won't necessarily be that quick. All right. One question. I mentioned that the functions, the predicate or mapper functions could potentially be asynchronous. Which means that we are basically always awaiting their result. I think especially for filter, it's going to be quite common to pass it a synchronous function. Sorry, to pass it a function which is synchronous. Like a predicate function for filter that just returns true or false right away. The question is, we know that primitives can't be Promises. So we could choose to skip the microtask tick in that case. It's not like this is, as it were, [unleashing Zalgo](https://blog.izs.me/2013/08/designing-apis-for-asynchrony/). There was these things always happen in response to some other Promise settling. So we are always basically in our own turn of a microtask anyway. So the question is, do we want to pay the additional microtask tick even given that we have already paid at least one? And my opinion is we should not pay that, which is to say, I think we should have a fast path when the result is a primitive. No one will ever notice this except that their programs will be faster. But before we move on, anyone have an opinion? + +DLM: NRO on the queue. + +NRO: Yeah. I have a question. What does currently happen if I install string.project.10? + +KG: It is not awaited. It is not invoked. The await logic checks for an object before the lookup of .10. + +NRO: Okay. Then I personally think it would be fine to have the fast path. + +JHD: fast path: Yes + +KG: All right. Hearing no further opinions, I am glad we agree. Next question. Right. So there's this question of what do we actually do? If the underlying iterator is incoherent. So let's do our map example again. That wasn't the incoherence. There we go. Our map example again. What do we actually do if this comes up? There's and there's two possible sequential orderings that this could happen. We could have a done true and then sequentially the thing which is sequentially following also temporarily follows with a done false. But we could also have the other ordering. We could have this thing which sequentially follows the temporarily resolving previously to the done true. What do we do? The let's see. It's maybe played out with filter. What do we do with this value? That I've indicated with a purple box here. My preference is that in the case that the `{done: true}` occurs temporarily second, we translate that `{done: true}` to an error. So it's not what I actually have implemented here because this was an open question. But in this case, we can see that we got a `{done: false}`. And then we get a `{done: true}`. We could say we know there is incoherence here. And we are going to instead of resolving the corresponding thought with `{done: true}`, we could put an error here that says you've done something wrong. We cannot do that when it is well, we can't necessarily do that when the ordering is the other way around. If we get a sorry, get a non the other way around. If we get something like this, we really do want to get the `{done: true}` right away. We don't want to have to wait for the other things to settle. So what do we do with this value? A reasonable thing to do is to say that in this incoherent case, we are going to get an unhandled Promise projection event. Which is sort of the you were doing something in some logic that no one is paying attention to anymore. And that's true. That is what unhandled Promise rejection is for. We don't necessarily have to do anything. We could just drop it on the floor. Filter is maybe a cleaner example of dropping it on the floor. Yeah. What do we do? Open question. + +MM: Yeah. So I'm still in favor of defensive. So disagreeing with WH's stated position. I think that the producing coherent output, however we define coherent, even against incoherent input, is the preferred solution certainly it's possible in this case at the cost of some concurrency. But as. + +KG: No, no. It's not just at the cost of concurrency. This is at the cost of delaying things arbitrarily, possibly infinitely. + +MM: I don't understand. + +KG: So here. Here's an example. We're doing a filter. We've done yeah. We've done our tuples. One of them rejected, whatever. We do a third pull. And then this value from the underlying iterator settles with `{done: true}`. What do we do here? If to be defensive, we would need to not resolve either of these two Promises. Until this fourth one settles. Even though we know that there is at most one value we could get from the underlying. + +MM: I'm sorry. Are we talking about map? + +KG: This example is filter. + +MM: Filter. Okay. So I see. So the first one, the filter, the predicate gave false is what you're trying to show here. + +KG: Yeah. Sorry. It's not actually important that the predicate gave false. I can do an example where it doesn't. + +MM: Well, I mean, I just want to understand a relevant scenario. The first one returning false is a fine relevant scenario. + +KG: Yeah. Okay. Let's do that one then. Yes. It returns false. We do another pull. Yeah. We do a total of three pulls from the result, which results in an ultimately four pulls from the underlying. And then the third pull from the underlying settles with `{done: true}`. Which means that because we have rejected one of the values from the underlying, and we've settled with `{done: true}`, at the third position, there is at most one value that we can ever get from the underlying assuming that underlying is coherent. So the thing that I have chosen to do is say, we don't need to wait for that fourth value. We know that we get at most one value from the underlying. We can settle the latter two Promises from the result immediately. But if you want to be defensive, we can't do that. + +MM: So I don't understand why you need to settle the on the output, the second `{done: true}`. Why I mean, once you know you only have one output, then just settling the first or settling or reserving the first for that output, and then the second is a done true. And then there is nothing. I don't understand why that's not a viable answer. + +KG: What do you mean there is no third? We've done three pulls from the result. + +MM: I see. I see. I see. Okay. Done three pulls. Okay. Right. So right. So any oh, right. A coherent iterator after it's done a `{done: true}`, all further pulls would still be done true. So what you're showing I now understand this. What you're showing is correct. And why is it a problem? + +KG: Well, because we can't be defensive anymore. + +MM: Why can't you be defensive anymore? + +KG: We've got this Promise that's sitting here, unresolved. And let's say it resolves with something that's not done true. What do we do? There's no Promises anymore. There's zero outstanding Promises. + +MM: Oh, sorry. I thought when you said that we know we're only going to get one result, I thought that that meant that the predicate had already returned false for one of the first two inputs. + +KG: It had. Yes. + +MM: Okay. I don't understand why if the predicate had already returned false for one of the underlying inputs, is the open question that you're asking is the fact that we don't know whether the other underlying input before the `{done: true}` is true or false? And that's the yeah. I'm not getting why there's a predicate. + +KG: Okay. Let's do a simpler scenario. Let's do a simpler scenario. We do two pulls. The first one immediately settles and this is dot filter. The first one immediately settles with `{done: true}`. This means there were no values from the underlying iterator. + +MM: Yeah. + +KG: The behavior that I have implemented in filter is that we then settle both of the results, both of the pulls that we've done with `{done: true}`. + +MM: Yeah. + +KG: But you want us to be defensive, which means I assume well, the open question is, what do we do if the underlying iterator gives us a value which is not `{done: true}`? There is nowhere to put that value. We've obtained two Promises. Those have both settled. There's nowhere to put this value. + +MM: Yeah. I think coherence dominates don't lose values. + +KG: This isn't just about don't lose values. What is your question about where this what is your answer to where this goes? + +MM: We drop it. + +KG: We drop it. Okay. Because we could say it's an unhandled Promise rejection. + +MM: Yeah. I think we drop it. + +KG: And not fire an unhandled Promise rejection. + +MM: Yeah. + +KG: Okay. So perfectly valid answer. + +MM: Okay. Good. + +DLM: WH next. And we only have about five minutes left. + +WH: Yes, we should drop it. And the other cases as well. + +???: In the other cases, meaning that if you get the. + +WH: Like in the map example. + +KG: Ah, okay. Yes. That was oh, I forget which. In this example, you think we should drop the third value? + +WH: Yes. + +KG: But if they what if they had settled out of order? If this one had settled first? + +WH: Well, okay. So I'm fine with either dropping it or passing it through if that happened first. + +KG: I mean, it's perfectly reasonable to say that you have different answers depending on the order in which these events occur. You could say that if the `{done: true}` happens first, then something which sequentially follows is dropped. But if the `{done: true}` happens temporarily second, then you might have already gotten a value. But you can't drop it in this case. You've already gotten the value. + +WH: Yes. + +KG: So your options are either say it's fine, and this is just incoherent. You are passing along the incoherence. But you could also say that you put in you emit an error in this case. Either is reasonable. + +WH: Yes. They're both reasonable. Propagating incoherence is fine. Dropping values in incoherent cases is also fine. Do whatever is cleanest for the implementation. + +KG: Okay. Great. + +RGN: I guess I'm left largely agreeing then as well. I think that having different results based on the temporal sequence is fine. We can eagerly settle Promises based on an assumption of coherence. And if that assumption is subsequently violated, then propagating the violation is fine. And is likely the most reasonable course of action in that case. Meaning it's the simplest thing that the implementation can do. + +KG: Okay. + +MM: Okay. So I continue to think so an answer to your question, I prefer a temporal order independent defensiveness, which means that you don't produce `fC` until you know whether the earlier ones were if there were any `{done: true}`s earlier. And if there were, then you never produce `fC`. Earlier sequence earlier. If there's a `{done: true}` if there's a `{done: true}` sequence earlier, then that third pull has to also be `{done: true}`. + +KG: I'm also fine with that. I think it's a little unfortunate not to get results as fast as we possibly can. I'm a little bit annoyed with any scenario where we are paying a cost for well-behaved things because of something about ill-behaved things. But we could choose to do so. + +MM: The case only comes up for map, correct? In filter or flatMap, you have to wait anyway. + +KG: Things are complicated in flat map. For filter, that's true. For a flatMap, oh boy. Let's play out a scenario. Let's do people's. + +MM: So let me just first respond to filter. For filter, that's true. Which is the consequence of not doing the early binding is that map acts in a way that's more expected given the necessary behavior of filter. + +KG: Okay. Oh, okay. + +DLM: Point of order about time. So I don't know, KG, what do you want to do? And the next agenda item, I believe, is an hour as well. So perhaps we could. + +KG: Yeah. I would love to go through the rest of my questions. But I should have put a two-hour time box on this. We are not getting through the rest of my questions. Let's try to play out this specific question, which might be another hopefully not more than three to five minutes. + +ACE: I'm happy to yield some of my topic's time. + +DLM: Okay. Thank you, ACE. Okay. So I was finishing this last topic, and then we'll move on to ACE. + +KG: Okay. Mark was saying he thinks that we should not do eager delivery for a map because that's more consistent with filter. All right. Waldemar. + +MM: Thank you. Because it's more consistent with filter and produces a defensively coherent output. + +KG: Okay. Yes. + +WH: I disagree. Suppose you have a case of an identity map in which the map takes the identity function, and you optimize it out to do nothing at all. So then you will get the incoherent result. + +MM: I don't understand. + +KG: Oh, WH is pointing out that if you use the identity function as an argument to dot map normally makes that whole operation, the identity. And with your proposed change, that would no longer be the case. You are no longer getting the same results that the underlying iterator produced. + +MM: Yes. So I would be in favor of if the function is the identity function, that we're still defensive against incoherence. And that means that the overall output is not identical to let's put it another way. The output is identical to the input if the input is coherent. And if the input is incoherent, then the output is still coherent. So it's only deviating from identity output in the input incoherent case, which I think is fine. + +WH: I don't think that's fine. + +SHS: agree with WH, garbage in garbage out. + +KG: I am personally also more inclined to agree with WH; I don't like to make people who are holding it right pay a price because of people who are doing something bad. + +KG: Okay. I'm going to wrap it up there. I had several additional questions. I guess eager delivery for map was one of the open questions, but I had several additional open questions beyond that. And if we find ourselves with an extra block of time tomorrow, I would love to come back with this. But obviously, I'm not going to make us come back for a fourth day with this. So probably you will just have to remind yourselves of all of this at the next meeting. Okay. Thanks. + +DLM: All right. Thanks, KG. + +### Speaker's Summary of Key Points + +* There is now a draft userland implementation and corresponding demo, with a bunch of interesting scenarios preloaded plus an interactive option for exploring yourself, at +* The following principles guide the design: + * order-preserving + * helper owns its underlying iterator(s) + * don't lose values + * getting { done: false } later in the sequence than a { done: true } is considered incoherent +* There remain a variety of open questions +* MM is inclined to be defensive against incoherent underlying iterators (i.e., to delay resolution of other values as necessary to ensure the result iterator is never incoherent even if the underlying ultimately appears to be); WH and KG disagree + +### Conclusion + +* the committee supports skipping `await` for primitive values returned by the mapper/predicate functions +* other questions remain open +* we definitely need a lot more time to discuss this + +## Composites update + +Presenter: Ashley Claymore (ACE) + +* [proposal](https://github.com/tc39/proposal-composites) +* [slides](https://docs.google.com/presentation/d/1vTRHdRzIZQF69Q5Nx_VDkCkm2mn1rJK-CTtDiSqA9x0/) + +ACE: Excellent. So a quick recap. So we last talked about composites in plenary back in November. Last year. And then also in April last year. And it's a follow on this kind of topic is a follow-on from records and tuples. So in a way, we've been talking about this space for, I don't know, at least five years. Maybe getting up to 10. Somewhere around that space. So I do want to recap things to get people up to speed. And definitely happy to answer general questions. But on the flip side, I do also want to make progress. So let's see how we do. But I do want to make sure everyone can follow along. + +\[small break to adjust slides] + +ACE: So this is the example I've shown before just to kind of motivate some of the conversation. So right now, if you create objects, even if they're frozen and would ignore the fact they don't have a null prototype, if you put them in a Map or a Set, then actually, I've gone ahead. If you put them in a Map or a Set, then they are treated as two different things because set just recognizes object equality, which is pointer equality. So this is sad. The most common way we see people fix this is using JSON string of thigh because it's just their built-in to the language. And in this particular case, this it kind of works. And so now I set has just size one. But now the sad thing here is that JSON stringify of thigh is risky because it only works like the order that you inserted the properties into the object will impact the JSON stringify of thigh order. So you can get different strings accidentally. Not everything can be turned into a JSON string like big ints. Some things just change like `NaN` becomes null. And flattening everything into just one big string can allocate a lot of memory. + +ACE(slide #5): And then at the end of the day, you have a set filled with strings or a map where all the keys are strings. Rather than a richer object. So if you're then going to iterate over it, you might have to pause or have a lookup table to get back to your actual original richer data. + +ACE(slide #6): So what this proposal does is say, how about we have some API? It's for composite here. The name could be something else. But some API, some data type, where you can construct these objects. And then inside, sets and then map keys they are treated as equal. And it's not shown here, but these objects would still be good, useful objects where you can still access `.x` and `.y`` on them. + +ACE: So the big design question that we've talked about before is exactly how do we achieve this desemantics? Because there's two different core ways you can implement it. And depending which one you pick, really impacts basically everything else about the other design decisions. You can't really talk about these other design decisions without getting past this first design question. + +ACE (slide #8): And that question is whether or not to use interning? So not interning. And the way that I initially had talked about this proposal and the current thing in the proposal repo is that every time you create a composite, maybe you use new and you always get back a fresh object. So they are not pointer equal. But when you use them in particular APIs, those APIs will detect that you're passing in a composite. And do the equality. So there could be an actual just a direct way of asking if they're equal. And yeah, sets and map. And then also array includes. Because that's the kind of three places where we use the same value zero at the moment. + +ACE (slide #9): So the way you would do that is the actual constructor doesn't really do much apart from create the object. But then when you have these APIs, those APIs need to add new branches. That detects what you're passing in. And then do something different. + +ACE (slide #10): The alternative approach is you intern and effectively move all of the logic, all of the implementation, just everything into the construction site. So when you construct the when you say, I want a composite where x is one and y is four, it creates an object. The second time I call this, it's looking up in a secret internal cache saying, we already have an object or we already have a composite that is exactly that construction. So it returns literally that object. So these are equal because they are literally the exact same pointer. And then just everywhere else in the language, they will be equal because it's impossible to not say they're equal. + +ACE (slide #11): So that would roughly look something like this. There's some secret cache that has weak semantics. So that it doesn't just leak forever. And all of the work is in the constructor kind of looking up, do we already have a composite based on what the caller is asking for? If we do, we just return that voice we create it. And you probably will need some extra logic in your GC to ensure that cache doesn't just grow forever. + +ACE (slide #12): So I had wanted a look at the performance implications of these because if you do some just back of the napkin calculations, it's kind of it's hard to think about you could kind of argue either way which one would actually work out faster. So I was just wanted to implement it to see actually how would it behave depending on how it's implemented. + +ACE (slide #13): And I presented this before back in November. So this isn't on its own isn't necessarily new. So it's just a very simple benchmark where we create a composite that has x, y, and z. And then it just kind of fill a 3D cube of this kind of creating all the numbers. Constantly creating a key and putting them into the set. And then loop doing that multiple times. + +ACE(slide #14): So this `makeKey` here is how are we going to make the key? Do we just call JSON stringify? Do we create a new composite every time? Or do we do an interning implementation approach? And what we can see here is that to begin with, the non-interned composite key was the cheapest to create. Doing the interning approach has a higher overhead, which is kind of what you'd expect because it has to do more work. But then when you then keep kind of doing that loop, creating those same composites again and again, the interning one gradually because it's now just being able to return the existing composites that it's already made, that kind of initial overhead starts to be slightly amortized. And then it ends up eventually kind of after the iteration becomes about the same speed. And then it ends up being faster. But they're very, very similar. And both were faster than JSON string of thigh, which was nice. + +ACE(slide #15): So that's what I represented before. But one of the questions that came up was this is a really, really simple benchmark actually. It just has three numbers. So since then, I've now done a new benchmark where the composite has those three numbers, but then also 97 other just static string keys that it's still having to take into account. + +ACE(slide #16): So this is a new graph, which looks very similar to the one I just showed. You can only really tell them apart if you see the before the numbers were even after the 10th iteration where I'm creating this is like 10 iterations of creating I think 125,000 keys. We're still only at less than half a second. Now that the keys have 100 components to them, it's now taking two and a half seconds to create those, create all those keys 10 times. But the kind of shape remains pretty much the same, which was nice to see. + +ACE(slide #17): But then I pushed it even further. And then was like, okay, well, now let's create a key that has 1,000 components to it. So again, it just has those three x, y, z numbers changing. And then all the other 1,000 and 21 keys are just these static strings. In this one, things actually switch over. And the interning case the slope of it just doesn't quite help enough. And it never becomes the. Interning approach always wins. Though they're still very, very close. And it still remains in this particular bench case, in this particular implementation, better than the JSON stringify. Which is, I think, good because if one of the intentions of this proposal is this could replace some of the use cases for JSON stringify, we'd hope that that would actually be a good thing we can recommend and it doesn't come with this caveat that actually it's might be slightly more ergonomic to use, but a lot slower. + +ACE (slide #19): The main thing I should point out is that the first time I implemented this, they were not faster than JSON stringify. They were slower than JSON stringify. So we see this one here. This is if I have three keys, 16 keys or 250 keys, but the patterns most visible in this one so the orange and then the second column and then this third column which we people can see colors is orange and then a darker orange. And then a green and a darker green. The to the right darker column is the same approach, but without a key sorting optimization applied. So the main thing I noticed is that as you have more and more keys, you basically just you're spending all your time. Sorting the keys. And JSON stringify doesn't have to sort the keys. So it doesn't have that overhead. We have to sort the keys to have consistent equality. So what I did is as long as you're passing in an object that actually has an internal hidden class shape, then I cache the sort for that shape. Similar to optimizations that are already there for object or keys already pre-knowing the enumerable strings of an object shape. And so that optimization was the biggest win. There were other little things I did, but nothing had as big an impact as doing that. So just something to be aware of. But the optimization was applied both to interning and non-interning. + +ACE: I see MM read his things in the queue. So before I continue, MM. + +MM: Okay. So when you're showing these benchmarks, does that include GC overhead? + +ACE: Yes and no. There are small pauses for GC happening. But because I don't think it ever triggers a full GC. So I did want to try and do some more examples to see if there was a big GC difference. The amount of GC time in all of them was relatively not a major aspect of the time. But yeah, it's good I don't think this fully captures the GC cost. It's the most accurate answer. + +MM: Okay. And does the implementation for interning GC when there's no references to the interned item, does it collect the interned item? Which would require something like weak tables. I mean, weak tables. + +ACE: Yeah. So for the interning table, it's storing them in weak refs. It's not actually weak refs. It's something lower level than that. But weak refs so that yeah, if the interned key is no longer held by anything, it can be collected. + +MM: Okay. Good. And my other question is, I don't understand the combination of intern and no cache. The interning table itself, is that not the cache that you're referring to when you say caching? + +ACE: So yeah, yeah, this is poorly worded. No sorting cache. So this is purely the difference of if you have an additional cache which lets you kind of not have to keep sorting the keys every single time you ask for a composite. It will see like, oh, I've already seen the shape of the object. I already know exactly what the sorted keys are for that. + +MM: Okay. And I'll go ahead and ask my question. I think we can probably take care of it quickly. Which is, the interning option must not create a communications channel for registered Symbols. We were very careful to specify registered Symbols so it did not create a communication channel. And part of that is that you can't tell whether the registered Symbol was already registered or whether this is the first registration. And the other thing is that a registered Symbol is completely immutable. So there's no ability to mutate it and communicate to someone else with the same registered Symbol. Is it the case for the composites that they do not that they inherit from null, or i.e., they don't inherit from something pre-existing? That they are fully immutable, and that there's no way to tell whether this is the first registration or a registration that's reusing something that was already registered? + +ACE: Yes. So beyond timing attacks of so yeah, assuming yeah, ignoring timing attacks, yes, it wouldn't expose a way of being able to sell the difference. + +MM: Okay. Excellent. That was all I had. I'm in favor of interning. + +KM: Yeah, I guess it'd be good to see a more robust workload. I don't know. It's kind of interesting, but it's still pretty micro benchmarky. And that has all kinds of consequences and various ways in different engines. I don't know of a great workload off the top of my head. I mean, I do know that sets and maps are used in a bunch of for example, the Jetstream benchmark. I don't know if they would apply in a way where you have to keep looking up the same key or they do anything like JSON stringify. I don't believe they do. But it would be good to see something more robust, I guess. + +ACE: Yes. I agree that it would be good. I also am not there's things I'm personally interested in testing, but I am primarily interested in what the committee would feel if they're unsure of which approach they would take and they can't decide unless they see a particular benchmark, I kind of I don't want to second-guess which benchmark people would find convincing because I'm personally convinced I can create a benchmark that makes any of these different approaches win depending on how I tune things. I think if you're if a composite is a more deeply nested, then the interning approach will do way, way better because of the kind of repeated lookup cost and also if I can tweak the kind of hashing to prefer different inputs so it's I have a personal preference, which I'll be able to invite which I want to win so I'm also trying to avoid that bias leaking into me designing a benchmark knowing which one I think it will show most favorably. But yeah, I'm certainly happy to put the time in, but I'd really yeah, I'd like other people to say, "Hey, here's a really interesting thing. Using a different approach for keying, could you re-implement it with composites? Different approaches and let us know how it goes." That would be much appreciated. + +MM: Yeah. I just want to emphasize I strongly prefer interning and not for performance reasons, although I like your nested example as a strong performance reason to prefer interning. The reason I prefer interning is we don't have to then revise the specification of maps and sets and includes. And I find that very, very appealing about the interning case. + +ACE: Yeah. Yeah. My largest takeaway from these is that the performance to me, it's not whichever is the fastest one wins. To me, they're both acceptably fast. Looking at the numbers I was able to achieve, this is creating millions of them. And the fact that people so I'm convinced either one is good from a performance perspective. But the additional advantage that you've just stated. Would not introduce a new I think fourth or fifth equality into the language. To me, it makes the sub way easier semantics to explain to developers for them to understand. And also from the thing I showed back in November was if the interning approach being completely scoped to the constructor makes it much better for the polyfill case. For the polyfill case, if you have to patch `map.prototype.set` and `set.prototype.add`, it's really, really bad for if you use a land versions of those methods so it's yeah, the fact that interning makes it easier for polyfills not having to patch all the rest of the parts of the language for the actual native implementation, it's much more scoped. And for developers, the equality kind of equality is already complex in the language and this has the simplest equality to explain. + +MM: Yeah. Perhaps if we go with the interning semantics, the name of the function should just be intern? + +ACE: Yeah. Yeah, yeah. I'm certainly not tied to the word composite. + +MM: Okay. + +DLM: Steven's on the queue with plus 100 about not revisiting map set spec end of message. + +ACE (slide #21): Great. So yeah, so weakness so assuming you do go with interning then I think we talked about again before is kind of what happens if you put these things in a weak position. Right now, anything that's type of object and the proposal is these would have a type of object and not a new type because that was one of the issues with records and tuples. What happens? So we say you can't put the number 42 in a weak set because you can always create the number 42 in the language it's like it has an internal lifetime. So by the same kind of logic, a composite that only contains a key where the value is 42, it also has nothing that kind of limits who can create this identical composite. Ignoring the fact you could delete the constructor and then no one could create any more composites ever. People can always just grab the number 42 out of thin air. They can always create an object with the key value a prop answer is 42. So someone can always create this composite. So it kind of has to just leak if you put it in a weak set. There's absolutely nothing we can really do here. Because at any point in the future, someone could come along and ask that weak set do you have that? So we spoke before and we did a temperature check and the committee was overwhelmingly in favor of we should just throw in this case because it's there's no it's just a guaranteed leak. People can just put this in a regular set and get the exact same semantics. + +ACE(slide #22): The more maybe perhaps more interesting cases so if you create a composite that does contain something that has a finite lifetime, then that now limits once that object goes out of scope, then we know for a fact no one can ever create a composite containing that object. So in this case, technically you can kind of imagine that this is more of an okay case. You could put this composite in the weak set and it could theoretically be removed from the weak set in the future and it's kind of unobservable you've done that optimization because no one could ever ask if anyone could ever ask you if that weak set has this composite, then by definition, they kind of have a reference to this object. So we could technically allow this. I'm kind of happy either way because if we on one side, we could just disallow this. So that we say for simplicity, you can't put any composites in a weak set. And if people do want these semantics, you can rebuild this use case out in user land, which is how exactly how these types of things are built in user land today. It's how the records and tuples proposal was built in the polyfill where you create a weak map try of all the weak things so that you can kind of deconstruct your key into the parts of it that are weak but those in weak maps and then hold on. So this doesn't add necessarily a new capability to language, but it probably would be more efficient for the engine to do it than to do it for a series of weak maps. But it also this isn't trivial to do natively either. You don't just get this for free in V8. You'd have to or any engine you would have to do work to implement this. So I'd be curious if people have strong feelings either way because I think kind of I think either side can be justified, but obviously would need to make a choice. We can't just have a global setting that says whether or not we do one or the other. Mark? + +MM: So rate shared Symbols are again, I think the closest precedent. Right now, what is the specified behavior for maps and sets for either an unregistered Symbol used as a key or a registered Symbol used as a key? + +ACE: I presume you're talking about weak maps and sets. + +MM: I'm sorry. That's what I meant. Yes. + +ACE: Yeah. So you can't put a registered Symbol in a weak set. You can only put unique Symbols and then, unfortunately, also the built-in Symbols like `Symbol.iterator` and but you can't put yeah. You can't put registered Symbols in because we treat them as equivalent to strings. + +MM: Okay. So I think that that's the right precedent and that the last question on your slide is this allowed? I think the answer should be no. And then we can just skip all the implementation complexity of trying to react to the garbage collection of inner objects. We can still decide the engine can decide whether or not to internally garbage collect that entry in the interning table based on that. But we wouldn't obligate the engine to do that. And by not by disallowing that last case, we don't make the engine's decision about whether to do that or not observable. + +RGN: Well, yeah. What would actually be disallowed in this case? What references sort the composite into a different identity? Because I assume that objects appearing as inline literals rather than variable references would be allowed. So I'm curious about what the proposed semantics are there. + +ACE: Yeah. I mean, so yeah, my weak preference is we say this isn't allowed. No composite is allowed. But if we were to make it the distinction, the distinction would be that at least one member of the composite can itself be put in a weak set. + +RGN: So you're saying if it's anything other than entries where every value is primitive? There could be no nested structure? + +ACE: So you could nest as long as you are nesting composite. So it would be like a recursive rule. So a composite can be put in a weak set if at least one of its members could be put in a weak set, including if that member was itself a composite that was allowed to be put in a weak set because it has a member. So if you had a composite of a composite of a composite of a composite that eventually resolved, so just an object, a regular object that can be put in a weak set, then that property would transitively apply all the way up the chain because of the recursive definition of the rule. + +RGN: Right. But made explicitly so. You'd have to actually spell out composite for every value that was not primitive. + +ACE: Exactly. Yeah. So the composite kind of constructor or factory is not a deep thing. It is just taking a flat key value. It's not it doesn't deeply traverse the object you pass in and compass it a thigh everything within it. + +RGN: Right. And that holds for arrays as well, correct? + +ACE: Correct. Yeah. + +RGN: Like an array is just as invalid as an object? + +ACE: I mean, I don't understand the question. + +RGN: Okay. So if the values must be primitive or composite, then that rules out plain objects as indicated with braces for syntax. But it also rules out arrays with brackets for syntax. + +ACE: I mean, to be clear, the thing that's so you can create a composite of any values. You could create a composite that contained objects and arrays and functions that thing that's being asked is like, what happens if you pass that into a weak set? So yeah. + +RGN: Yeah. With that scope in mind, that arrays and objects are not differentiated. + +ACE: Yeah. So yeah. So if you had a composite where one of the values was an array because you can put an array and array inside a weak set, then technically we could say, well, that composite can go in the weak set because it can inherit a lifetime from that array. But again, I think the simplest answer is we say all composites not allowed. + +RGN: Yeah. + +ACE: Regardless. Yeah. + +ACE: MM? + +MM: Yeah. So clearly, if you're disallowing composites there, you enable the observation about whether something is a composite. And you also enable that observation because with the interning, because if you just create it again, you compare them, you can see that it's the same as something that you just created. So would there be a also a predicate to just make the testing convenient since it's not enabling something that was otherwise unobservable? + +ACE: Yeah. Personally, yeah. Hopefully, there'll be no objections to having a composite is composite or a interned is interned. I would I think that would be a nice thing to include. + +MM: Okay. Good. I agree. + +ACE(slide #24): Great. So another design thing that comes up thinking about this is what do we do about this case? If I create a composite where I say I want to composite where the key A is equal to 1, and then I pass in an object where now I'm saying I have A is 1 and B is undefined, are these the same key or a different key? From one side of kind of the thought is there's no distinguishing between a key being absent and a key being present but set to undefined. Can be a bit of a foot gun because it's very easy to create this, especially even if you have if you're using things like TypeScript if you say that I have some interface where B is optional, then I can depending on your TypeScript config settings, I can say I'm going to create B in explicitly set it to undefined. Or I'm just going to emit it. So personally, it feels like the thing that will do the most good is we drop this and we treat them as equal rather than trying to actually treat these as two different keys. This is similar to what already happens if people are using JSON stringify, JSON stringify will just drop undefined values. And as I said, I think in this particular case, telling people that there is a semantic a very, very important obviously, there is a semantic difference between a key being missing and being there but being undefined. But the implications of that and how significant it is, I prefer not. If people do want to distinguish these cases, I think a better pattern would be having part of your key be a discriminator that's saying type person or type manager where rather than just using the kind of a collection of keys themselves as the shape, I think that would be a better way if someone does want to treat these differently. I imagine those use cases would be better served by something kind of more concrete but yeah, very, very happy to hear people's opinions because I do think this is a reasonably interesting part of the design space. + +WH: Just a clarifying question: When you create a composite like in the examples here, does the composite only look at its own properties or does it also look at properties inherited from the prototype chain? + +ACE: Only own properties. + +WH: Okay. + +MM: So it's weird that you can't composite or intern an array what you could do is just not notice that it's an array and just do the own properties. Specifically, the index own properties plus length. And just do it as just do it as if you're interning an object with those own properties. + +ACE: Yeah. I mean, I could also I think at one point, maybe this was not during plenary, but just during a lunch break. I think we did talk about potentially you could have composite could see that you're passing in an array and then return interned arrays to me, the biggest the two yeah. I don't like it because you now need a prototype and it now makes other conversations about realm, cross-realm interning kind of it forces that. + +MM: Yeah. Exactly. I think that it can't the result of interning an array cannot be an array for that reason. But it could be an object that had the same own properties as the array. + +ACE: Yeah. Exactly. And I also think in general, because the performance of these things kind of scales with their length that encouraging people to create interned arrays clever people doing the right things, I think, will do will use that wisely. But I think it could also be a bit of a foot gun if people are thinking all this is like a functional design pattern and I can just keep pushing the end of an array and interning it and it'll under the hood use some persistent data structure or linked list and it's like, no, no, no, it's interning the entire array from scratch every time. So yes, I think for a variety of reasons, yeah, not supporting arrays but letting people create array-like structures is the right choice. + +MM: Good. + +MF: Okay. Maybe I missed this part in your explanation here, but I mean, it seems obviously true to me that these should be different, that undefined is like a value and V would be a key of this composite. What is the case for dropping them? + +ACE: The case is mostly, as I think this will so in this literal case when the code is written like this, yes, it feels like these should be different. But I don't necessarily think this is going to be how people are always creating these things. They might be creating these two things at two different call sites based on an argument that's passed in where I think it could be really easy to say if you have a type interface where you say something is optional that sometimes someone is just omitting a property and sometimes they're doing it. If people are always creating composites literally like this, so that they can really clearly see the exact keys and then not passing in just a foreign object, yeah, maybe it's less of a foot gun. But I feel like this would be a foot gun if we don't drop them. + +MF: Okay. I think I understand now. So this would be like you want a value that explicitly indicates absence. I think that this is an existing issue already with people constructing objects with object initializers through parameterized inputs. I don't think that that's a problem we should try to solve in this proposal. And if we want to explore that, we should explore how objects handle it as well. For this case, I certainly don't think that we can use undefined to be that indicator. Just because undefined is a value like any other value. + +ACE: Okay. Yeah. Yeah. Yeah. I'm certainly happy to keep talking about this. I mean, I'd be curious on the actual use cases because if someone does want to create keys where they differentiate, then null is sitting right there and wouldn't be dropped. And I am the reason undefined is special here is because as we all know, that is the value you would get if you try and get a missing property and it's not on the prototype chain. So there is undefined isn't just like a randomly chosen value to be the sentinel. It's the most obvious value to drop because it is the same one you get when it's missing. But yeah, I'm happy to keep talking about this offline, definitely. Want us to make sure we make the right choice here. EAO? + +EAO: I think I'm with you in that undefined should be dropped because we should handle it the same way as we're handling already undefined in places like options bags or JSON serialization. Where undefined values are dropped, that is the reasonable thing to do here. + +ACE: Thanks. And KG? + +KG: Just the opposite opinion, basically. My understanding here is you put an array, let's say an array of length two, and let's say the first value in the array is undefined. I think that the resulting composite should have key zero with value undefined, key one with value whatever that was, and length with value two. If the resulting composite doesn't have key zero, that's very strange. That's like a holey array, basically. And I would be uncomfortable with that. + +ACE: Yeah. Yeah. Understood. As I said, I don't love people using this for interning arrays just because of the performance implications, but it certainly is something I'm sure people will do. And you're right that creating a holy array is something I do not love either. EAO? + +EAO: Yeah. I'd say for this one, we should do whatever JSON stringify does for arrays with undefined values in them. I think those are cast to null, but I could be wrong. But that seems to me like the closest existing analogy we have in the language and which is probably used quite often for the same sorts of purposes that composites would want to be used for where you create the thing that represents an object. Without actually being the object itself. + +ACE: Yeah. And you're right. It tends to null for a JSON array. MF? + +MF: I understand the desire to refer to `JSON.stringify` here just because that is a crutch that people use today to try to do the same thing. It's not like an inherently related topic. It just happens to be something that has some of the properties that we want. But I would not use that as an analogy. I would not use that to base any of our decisions on. + +ACE: JSL? + +JSL: Yeah. I'm just it's possible I just misheard, but in terms of how we treat the undefined in options bags, we don't treat undefined as missing. It's the same as missing in option bags. Cause is a good example on error. If cause is missing, if cause is missing, then it's not installed. Oh, okay. But it's still an existing case. So we can't we can't say that we're treating it the same. + +ACE: RGN? + +RGN: I'm hearing people's intuition being built up from things like `JSON.stringify`, and we just had a mention of holey arrays also. And I'm wondering if that intuition is dependent upon the output from calling Composite being itself an object with properties… if we imagine a similar model where the output from Composite is just an opaque value that has identity but nothing else, do those intuitions carry over or instead does it seem more reasonable, for instance, to treat present-as-undefined distinctly from absent? + +ACE: For me personally, I am purely thinking about this from the equality semantics. So even if these were opaque the footgun I'm concerned with is someone these things not being treated equal when they hoped that they would have been. But and that's regardless of whether they're opaque or not. + +RGN: Right. It just sounded like that might not have been true for everyone in the discussion. + +ACE: Oh, yeah, yeah. And it thinks it's good. I think what's most likely to happen is I think I'm hearing more people say we should respect the undefined and not try and be smart and drop it, which I can certainly be convinced for. I will highly likely do like a temperature check in a future plenary when I present just to kind of cast a net wider to kind of make sure that yeah, I'm going to move on from this particular bit just because. + +\[Teams just turned off suddenly] + +ACE: Great. Thanks. So yeah, there's definitely more things to discuss. I can see there's some things on the queue. With the testing I've done so far, I'm going to change the proposal to focus on using an interning approach. So if people are really, really against that, I'd like them to please get in touch, raise an issue or matrix me. Because that's going to be the change in direction. Also with not allowing them in weak maps. Then there is still the discussing about the undefined. And I think there's also conversation about cross realm. That came up from the SpiderMonkey case that we haven't talked about that some more. So yeah, I'll bring this back before asking for stage two. But that's the direction I want to go in. So please let me know. If you have issues or questions, further designs, love to talk about this. + +USA: Okay. Great. Thank you, ACE. Well, that's the end of our content for today. + +### Speaker's Summary of Key Points + +* A reminder of the proposal's primary motivation: having compound Map and Set keys +* Benchmark results of an experimental implementation in v8 were shown +* Both Non-interning or interning were the fastest depending on key size and iteration count +* The implementation needing an internal object key sort cache was required to be faster than JSON.stringify +* Also discussed allowing composites in WeakMaps, and if an 'undefined' value should key differently from an absent property + +### Conclusion + +* Proposal will pivot to an interning based approach, and not allow Composites to be held weakly +* Will continue discussing handling 'undefined' values +* The proposal will be brought back to committee before asking for stage 2 diff --git a/meetings/2026-07/july-22.md b/meetings/2026-07/july-22.md new file mode 100644 index 00000000..bd033792 --- /dev/null +++ b/meetings/2026-07/july-22.md @@ -0,0 +1,1145 @@ +# 115th TC39 Meeting + +Day Three—22 July 2026 + +**Attendees:** + +| Name | Abbreviation | Organization | +|---------------------|--------------|----------------| +| Chris de Almeida | CDA | IBM | +| James M Snell | JSL | Cloudflare | +| Waldemar Horwat | WH | Invited Expert | +| Richard Gibson | RGN | Agoric | +| Caio Lima | CLA | Igalia | +| Dmitry Makhnev | DJM | JetBrains | +| Olivier Flückiger | OFR | Google | +| Nikolaos Papaspyrou | NPU | Google | +| Lea Verou | LVU | OpenJS | +| Linus Groh | LGH | Bloomberg | +| Philip Chimento | PFC | Igalia | +| Devin Rousso | DRO | Invited Expert | +| Luna Pfeiffer | LPR | Invited Expert | +| Istvan Sebestyen | IS | Ecma | +| Eemeli Aro | EAO | Mozilla | +| Ashley Claymore | ACE | Bloomberg | +| Aurèle Barrière | AUR | Invited Expert | +| Clément Pit-Claudel | CPC | EPFL | +| Dan Minor | DLM | Mozilla | +| Jordan Harband | JHD | HeroDevs | +| Justin Ridgewell | JRL | Google | +| Kevin Gibbons | KG | Invited Expert | +| Keith Miller | KM | Apple | +| Matthew Gaudet | MAG | Mozilla | +| Michael Ficarra | MF | F5 | +| Mark S. Miller | MM | Agoric | +| Nicolò Ribaudo | NRO | Igalia | +| Shane Carr | SFC | Google | +| Stephen Hicks | SHS | Google | +| Ujjwal Sharma | USA | Igalia | + +## call for stage 2 reviewers for 'error code property' + +Presenter: Chris de Almeida (CDA) + +* [proposal](https://github.com/tc39/proposal-error-code-property) + +CDA: We missed, as we often do, one of the items that we must address once a proposal reaches stage two which is the committee needs to assign reviewers. Who would like to be a reviewer for error code property? I hear he's muted, I think. I remember JHD saying he would. + +JHD: Yeah, I'd like to be one. + +CDA: Okay. Anyone else would like to be a reviewer for error code property? + +RGN: I can review it also. + +CDA: Thank you, RGN. Very well. + +### Conclusion + +* JHD and RGN will be Stage 2 reviewers + +## Decimal stage 1 update + +Presenter: Caio Lima (CLA) + +* [proposal](https://github.com/tc39/proposal-decimal/) +* [slides](https://notes.igalia.com/p/tc39-2026-07-decimal/) + +CLA: Yeah, so I'm here presenting for JMN. JMN is one of the official champions of the proposal. I'm not a champion of the proposal, but I work with JMN at Igalia. And the decimal is a work between Igalia and Bloomberg as well. For a little bit of context and why I'm involved on this as well, I used to champion I don't know if it was official or not. I work on a decimal back in the stage zero. Like five years ago. So I have a little bit of context of the reasoning. But of course, throughout the five years, a lot of things changed since I came back. And Jesse was not able to attend today because of personal reasons. But yeah, let's talk a little bit about decimal. + +CLA: It's a nice time to talk about decimal because there is a lot of other proposals that are moving forward in things that used to be use cases for decimal, and it's important to clarify things. And we also would like to bring back the topic again to ask Plenary's opinion in some parts as well. + +CLA: Okay, so it's been a while that we talked about decimal. And I think it's important and from the discussions that I had some time, it's important to kind of understand which kind of problem statement we are trying to solve with the proposal. This is not like the problem statement that is in the repository officially, but I think it's like it's a good shape to try to explain what's the problem we are trying to pursue here. And so at some point. So the thing is that humans and mostly programmers, they used to think arithmetics on base 10 usually, which is essentially decimal. And never like they are doing in writing code, mainly in JavaScript, but in other languages as well. They carry this assumption of that the arithmetic of base 10 numbers will just work as expected. However, if you look for every single famous and mostly used programming language, actually the way to represent decimals actually don't represent decimals, but float point numbers in general is through float 64, which is a binary float point number format. And the thing is that the expectations and how those numbers work mainly on arithmetics breaks the expectation of most people until of course you get you dive deep into the ins and outs of binary float point number, you kind of try to grasp and understand what's going on in general. But what we would like to provide here and that is not really a way to do this in JavaScript so far, is that we would like to have an easy way to actually do those manipulations with decimals in arithmetics in a way that the models the reasoning and the thinking of most programmers in general. So right now, if you would like to do this in JavaScript, you basically need to get a user library and start doing things. In addition to that, if you look to the languages that are as high level as JavaScript, and even some that is lower level than JavaScript at some point, they do have they do provide this easy way throughout either like into the ESTL or other ways in general. But since that is not really a way to do this in JavaScript in plain JavaScript, you just don't have like interoperability in general. So the whole point of the problem here is that like it's not simple enough to do manipulations on decimal arithmetic. + +CLA: Try to ground a little bit this statement. I granted some cases of real-world evidence here. On how the expectation of people on binary float point numbers are broken in general. So this is one question that used to be the title of the question used to be like is JavaScript float point number best broken. But like after the 17 years that this question was actually opened, it changed to be a more generic one since the reason is not really JavaScript, but like the binary float point number in general. But this is one of the most viewed and pivoted questions regarding the float point binary float point math broken in general. So this is like this canonical case that I think in a lot of presentations before were used where you have the 0.1 plus 0.2 doesn't equal to 0.3. But yeah, I mean, the thing is that even though it's 17 years question, you can see that the last modification of the question was five months ago. And it's how familiar views for this question. So it's pretty popular. But going a little bit further here, there is this post if you'd like to see like the concrete examples in general you can see like clicking through the link. But there is this blog post from Ricky Reagan where from 2013 where Rick made a study to observe throughout 40 days how many questions for binary float point number and like misinterpretation and misunderstandings of binary float point numbers would arrive in this type of a flow. Which is kind of a question similar to 0.1 plus 0.2. And the thing is that throughout 40 days, there were he was able to map 14 questions. So if you look like it's 18 questions actually. So it's almost like every other day you have someone asking on this. I don't think we would get as much as questions right now on the second flow because the access of the second flow nowadays is not as big. But the thing is that like it tries to bring more evidence to this that the binary float point number actually breaks the expectation for most programmers in general. And of course, like this is not only for JavaScript. There is a couple of questions for JavaScript, but at any language that use binary float point number, Java, C#, and other languages as well, they have those questions coming up all the time. And I think here is a more concrete example of Nicholas one of the delegates here. Accounting software where there was this error probably like the programmer was doing some operations on numbers here. Thinking it was okay to represent basically money in general. And you have those rounding errors just filling up and flowing until the user experience. It's pretty common to see such kind of things in the wild whenever you are like using daily software. But yeah, the thing is that like it's there. And a lot of people don't know. They might be newbies. They might not be newbies, but the thing is that like software and bugs are appearing all the time because of like the misuse of the binary float point numbers. Thinking they are representing decimals at some level. Yeah, I mentioned like briefly that other languages that do have that, but I would like to reinforce this. So if you think language like Java, Python, C#, Swift, Ready, all language that you have some kind of you can do applications as you can do in JavaScript in general. They do provide a way in their standard library to manipulate decimals at some point. And even like languages like C and C++, there's like some proposal going to add decimal into the language as well. So what I want to say here is that like not having decimal in JavaScript, essentially make us have this lack of features compared to those languages in general. And you need to solve this in the user space. + +CLA: Okay, so given I gave some introduction of the problem, statement here, and recap, of course, like this is a stage one proposal and there's already a lot of if I five years of work basically of designing and try to pursue the solution at some point. And so far what we have for the decimal proposal is that we have this decimal 128, which follows the IEEE 754 standard. And the idea here is that like for the applications that we think having a 34 significant digits is probably more than enough to think in the use cases. I think the most strong use cases here is the financial one. But yeah, the thing is that like we have this representation of 34 significant digits of decimals. Of course, it's a float point representation. And the API so far is trying to provide a constructor of decimal. An object and you create an object decimal and this object has some methods to do operations like arithmetic and comparison as well. Trying to accommodate the requirements for performance. The idea is here is that like implementers don't need to keep normalizing every single operation. And we have this philosophy of trying normalizing the way out. We essentially means that whenever the decimal is going to reach the programmer's understanding at some point, we will normalize. So whenever you do a dot two string or also do comparisons, you would normalize those values and by normalization here we mean that 1.2 and 1.20, they would essentially like be treated as the same number whenever we have this dot two string or comparisons happening as well. And just to remember like this design came mostly from a lot of iterations that we have in Planetary and even outside Planetary as well trying to accommodate the discussions open into the repository. Of the proposal. And so on and so off. + +CLA: Okay, so one new thing I think that most of people involved on those numeric topics, they know. But like it's important to state is that since last time we talked in the Planetary, there were some advancements and some changes on proposals. That I think they relate to with decimal kind of deeply at some point. And to be fair, I think some of them are actually even better than what we had before. So if we take into consideration, for example, now amount and Intl keep to really zero, they essentially solve the issue of canonicalization and keeping information on trailing zeros and precisions and such kind of things. And like it basically makes the life of decimal easier. Takes out this use case but like takes the idea of like transporting and representing numbers with keeping your precision and doing some conversion, etc. much better. Also, in terms of trying to reduce the amount of rounding errors that happen in the binary point numbers, which of course is one of the major use cases for decimal, is the rounding errors that happens in binary float point numbers. We do have `Math.fma` and also some precise both of them are proposals that allow do float point numbers operations without a lot of errors in general. Which would solve part of the problems that we also have for having mining decimals as well. But the thing is that we believe that those proposals, they don't really solve the problem statement of doing arbitrary operations that arithmetic operations and manipulate decimals the way people were expecting general programmers expect in general. + +CLA: Okay, so the idea of the presentation was to be brief and actually focus more on discussions happening on the queue and etc. And the whole point of the presentation that I had so far is to ask these questions of do we want decimal on JavaScript? We as plenary as a committee, do we think that it's worth to implement such kind of solution here? And I think those were questions are key points here. So are the cases, the use cases that I presented here compelling enough to justify a new type to manipulate decimal? Is the data model that we have so far okay? And if it's not, what we need to change to accommodate things? Also, the way we are pursuing the design, is this okay to optimize? Or is it hard to optimize or okay to optimize? And I think one of the most important questions here is that like does the object-based API that we have so far solve the problem statement that I mentioned in the beginning of the presentation? And I think that's pretty much it. I would like to go over the Q and like spend the rest of the time discussing if we had like any discussions in general. + +NRO: Yeah, in the early days of amount, I know that decimal was kind of on hold also waiting to figure out the data model and amount. Now this amount is stage two. How does decimal fit into that? + +CLA: So I'm not sure if I will be like the best person to say that. But the best of my knowledge here, on amount. Amount is taking over one of the use cases of representing some numbers and the precision of those numbers and doing like some manipulations. And transportation at some point. But you really don't amount is not really enabling doing arithmetic operations. Which was like the major case for decimal in the beginning. So I think most of the reasoning of holding decimal on amount is regarding are the case use cases that amount is taking right now enough so we don't need decimal at all? Or do we think like decimal is still valuable to be working on in general? So that's essentially like what I think it is so far. I think the situation also is that and it's mostly like a personal opinion here, but we are having discussions on amount regarding how to do proper arithmetic operations in the decimal space, which we call mathematical value as well. So I'm seeing like a relationship with that if we had like decimal into the language. We could potentially in some cases build stuff from amount on top of decimal. But I don't think like amount is actually being designed to depend directly on decimal so far. It's open in the design to add decimal if decimal becomes a real thing in the language. But like so far, I'm seeing those things more related like that. + +EAO: The way we currently have amount, as it was accepted for stage two, is that it is capable of representing amounts wrapping any of the otherwise existing numerical representations in the language, which means that as currently conceived, if decimal were accepted, then the amount would be extended in such a way that it would accept decimal values next to number and BigInt and decimal string values. + +Decimal has been presented and conceived—thus far at least—with a specific upper limit on its precision of what it's capable of doing, while amount does not have a specified upper limit on the precision and does support or is intended to support also BigInt values. This means that accepting decimal into the language as currently conceived and proceeding with amount as currently conceived we could not use decimal internally as the only representation of numerical values, because a decimal 128 is not able to represent all BigInt values, for example. But if both of these proposals do advance it does seem sensible for us to continue having conversations and aligning these and this may of course mutate both of these or neither of these proposals as our conversations progress. + +WH: Amount solves a somewhat different problem. In Amount, what happens if you convert 0.1 yards to feet? This involves computing the product of the Number 0.1 and the mathematical value 3. The answer you get is 0.30000000000000004 feet because that is the closest Number to the product of the mathematical values of the inputs you actually passed into Amount. So Amount solves a different problem than Decimal. It solves it as best as we can but you will get surprising roundoff in cases where the values you want to pass in can’t be represented exactly as Numbers. + +NRO: Okay so right now the amount accepts any type of number but it does operations include 64 space. And so I guess if amount could use decimal to this internal operations, right? Even though EAO said it supports that amount supports a larger space of numbers, it seems like it still costs them to like the weird math one in some cases. + +EAO: Yes, if decimal is introduced then for conversions, it would make sense for amount conversions to be based around decimal, which would be in its context quite a bit more straightforward. While amount does not require decimal to proceed, it would be very good before the amount proposal reaches its final stages, for us to have an understanding of whether we expect decimal to be a thing or not. Not a requirement but it would be just very good. + +CLA: Does this address your question, Nico? + +NRO: Oh yes, it does. + +CLA: Okay. So I don't see anything in the queue. But I would like to raise one thing that is kind of important here. So we got some informal feedback in general of decimal as the way we are designing so far. And I'm wondering mainly like for implementers but also like from committee members if we try to pursue in the next scenario, for example, the current design that we have for decimal for stage two, would we have like any conditions from people in general? So I would like to ask like are we satisfied with the decimal that we have so far? Does anybody has any concern of the decimal that we have so far? + +JHD: Okay. Yeah, just wanted to continue to restate that I find the use cases very compelling but I don't think that the object-based API solves it and I think it needs to be a primitive in order to solve it. Like I like amount. I'm glad that's coming in. I'm happy with FMA and with sumPrecise and all that. And finding ways to make number stuff better is good but I like the ultimate solution in my mind is still not even decimal 128 just like full on number primitives that are actually just math. Just restating that for the record. + +NRO: Yeah, question here. I remember maybe two years ago I was happy with making sure that this decimal object is like forward compatible with potential primitives. We heard many times from browsers that they do not want to do more primitives right now because they're not convinced any of the ones have been ever presented that are like worth it. And so this proposal now was like going towards the direction where we can like retro explain it as just an object wrapper. Was that an acceptable path? + +JHD: I mean, I agree that that leaves a technical path open for primitives eventually. But given that the pushback about not wanting to implement primitives isn't a technical one, it's a like resourcing one, or a like prioritization one, I don't I think that will likely not even providing the technical path will still not increase the perceived prioritization from browsers or like in other words, I don't think that it will make the path easier. I think it will make it less likely to happen. Even though it's still a technical possibility. So I don't think that and I don't and if we never end up with the decimal primitives, and we ship the object form, I don't think we've done a good thing. + +EAO: This is a question for JHD; could you clarify if your concerns regarding the decimal as an object versus decimal as a primitive are such that your sense is that it is a blocking concern for entering stage two for decimal not as a primitive? + +JHD: Yeah. That's how I've expressed it in gentler terms. But yeah, roughly like that every time I think. + +JHD: Well, just to elaborate a little bit. What I mean is stage two means it's something we definitely want in the language. And I don't want the object form in the language if the primitive form isn't guaranteed to also be there eventually. + +CLA: Yeah, I think the only way to guarantee that is actually placing the same proposals as this one because if we do move forward with just the object, we want guarantee anything on the primitive form as well. Go ahead, please. + +JHD: No, I was just agreeing with you. Continue. + +CLA: Yeah, I agree with you. Like the primitive one would solve the part of the problem statement where like easy to use is one of the parts that we have. I mean, I do have some feeling the thing you like technically speaking, I also agree with you since I was involved on being implementation. Quite some time ago, I think like it's possible to do as well. I'm just wondering like if this conflicts with the feedback that we have from implementers, for example. So if you ever try to pursue the primitive path here, if what do we need to prove to browsers in general to allow this to be introduced to the language? + +JHD: I mean my understanding of the pushback is that BigInt was either some combination of too much work to implement and/or caused slowdowns that required more work to ameliorate or that were not mitigated. And the only way I know of that I can think of to address those concerns is to just implement it, implement decimal primitives in all the browsers and demonstrate that that's not the case, or that that's already resolved. That's a ton of work for a proposal that would need to be that would be still in stage one in that world. So I'm not suggesting that I'm not trying to voluntell anyone for that. And I certainly am not capable of doing that. But I believe that that is a that that would be a path to get to altering those objections. + +CDA: Okay. We are right about at time. I want to see if we can get through the queue very quickly. KM, can you be brief, please? + +KM: Sure. I mean, I think to clarify on the Bigint one, Bigint's cross-cut the entire engine. So every line of code basically in the engine you need to read and make sure like this thing is appropriate with a new primitive. Like every like anything that checks types, you need to make sure it fits in your like anything yeah, just so much code that you need to go through in order to make those work to like anything that as a new primitive needs to be clamored by like a huge percentage of developers to make it worthwhile, I think, in our opinion. But like as I mean, double-digit percentages of developers need to use it on a daily basis kind of thing. Which so far it does not that we don't believe that's the case for decimal. So I think that's our concern with having it as a primitive. + +DLM: Agree with KM. + +ACE: So I don't think blocking this proposal just because under saying I won't block it if we can Promise some other thing happens, I just don't think is a great way for the committee to run. So I don't necessarily get it if that wouldn't that. But I will say so we use decimal inside Bloomberg. Yes, it would be kind of cool if it was a primitive, but really it's fine being an API. Like the place is because we only use it in select places, the fact that they can't use operators and they have to use methods to add and do the things is honestly fine. Like it's I don't think it's important enough that it's a primitive. I think the object API will work absolutely like perfectly for the things and that also works for the engine's use case. It works for the yeah, I really would like to try and move past this blocking concern because I don't think it holds up its weight. + +CDA: All right. I know people have entered the queue, but we are past time already. Maybe we could do a continuation. But it's not looking great. Please do continue the conversation async in the repo or in the delegates chat. And we will be moving on to the next topic, which is Shane's topic. + +### Speaker's Summary of Key Points + +* Restating the problem statement of the proposal and presenting current Design and prior art for what would be the solution for Decimal. +* The presenter asks if with current design, what are the concerns from other delegates. + +### Conclusion + +* There are major concerns with the current Object API and not being primitive. There are also major concerns from browsers to introduce the proposal as a Primitive. Since there was not enough time to discuss everything on plenary, the conversation will continue through GitHub and other means. + +## Duration units in Amount and Intl Sequence Units + +Presenter: Shane Carr (SFC) + +* [proposal](https://github.com/tc39/proposal-intl-sequence-units/issues/8) +* [slides](https://tc39.es/proposal-intl-sequence-units/slides/time-units.html) + +SFC: Okay, let's go ahead and get started here. So this is sort of a hybrid proposal presentation. It's about Intl sequence units, but it also has questions that are relevant for Amounts. So the title of the presentation has both amount and Intl sequence units in it. And there's an issue on both repositories. So it's a little bit of a hybrid presentation about that impacts both proposals. + +SFC: So first, a little bit of a background. What are sequence units? So the Intl sequence units proposal that I presented at the last plenary goes over these. But you can but just to give everyone a reminder, when I say sequence units, what I mean is a unit that has multiple single units inside of it. So a very common so these are some very common ones. So for example, foot and inch. Someone might have a height of four feet and six inches. So that is it's measuring a single quantity, but it's split into two different units. So we call that a sequence unit, meter and centimeter, pound and ounce, kilogram and gram, just to emphasize that these are not just imperial, they're also commonly used in the metric system as well. So and this also can happen with time and duration units. So for example, hour and minute, minute and seconds, day and hour, those are also time or duration units. And there's maybe some subtle differences between when I between the word time unit and duration unit. I'm going to use them interchangeably for the purpose of this presentation because any distinction there is not relevant for the questions I'm going to be asking. But yeah, duration units, time units, one and the same for what I'm going to be talking about today. So let's talk about the status quo for time units in amount and Temporal. + +SFC: So time so duration units are already available on the web platform in duration to Intl duration format as well as Temporal duration and what's shown on the top of this slide is code that already works. Where you can go ahead and represent duration units using Temporal duration as well as format the duration units using Intl. And this already works on the top. So the question is more, what happened what happens on the bottom? So the bottom is since we have sequence units now, what should happen if you try to use one with Intl number format? What should happen when we try to use one with amount? So this is a little example of a code for that. + +SFC: So we discussed this in TG2. And in the numerics call, but we didn't what ended up happening with that there were two different perspectives that got surfaced with some delegates holding one perspective and some delegates holding the other. And I wanted to bring this to plenary to like discuss like the to discuss this because it wasn't normally when we come to TG1, we like to have like a recommendation. Like the TG2 has a certain recommendation that we discuss. But in this case, there were two perspectives that got surfaced. So I wanted to discuss both of those with the plenary. One of them is leaning toward not including the time units. And focus on guiding developers to the Temporal and duration. And the other is that we should include them to focus on alignment with CLDR and ease of use of amount. So I'm going to go through some of the arguments in favor of both of those positions. + +SFC: So first, I'm going to start with exclude time units. So position one, exclude the time units. So the arguments here, one is that duration units have a lot of footgun. So for example, day and hour crosses time zones or daylight savings boundaries. So and month and day, depends on calendar month length. So if you have those specific time units, then you start to run into issues because the two units are not always convertible from one to the other. Two is that durations are complicated to format. So for example, there's the digital formatting style. So if you just pass your duration sequence units into your Intl then like there's a lot of different settings that need to take effect also zero-valued fields. In order to get the correct behavior and we already figured this all out in Intl so argument three is that the web platforms to promote the right way to do things. We just spent nine years designing Temporal so we shouldn't add a worse way to do time units because no matter what we do in amount, it's not going to be as good as the way that we just did them in Temporal. And we spent a lot of time making sure that both Temporal observation as well as Intl observation format like do their jobs well and that they integrate well with each other. So we shouldn't reinvent the wheel. Option four, and this is one of the ones I'm most worried about, is that we explicitly decided to make a separate Intl duration format in the Intl duration format proposal. Instead of overloading Intl number format with the duration formatting options. And if we add duration unit formatting now to Intl number format, then like those lack of duration-specific features will be a pain point for years to come, meaning like people will be asking, well, I have an amount that has a time sequence unit that has minutes and seconds and I try to format it using Intl number format, but it's not giving me the digital form. It's not giving me five colon three zero. Like please fix. Right? And then our answer is always going to be, well, you shouldn't be using amounts and Intl number format. You should be using Temporal duration and duration format. Right? That's going to be our answer. And that's not a really great situation to be in. So I'd be having to keep triaging that for years to come. Problem five is amount is already more general than Intl number format. I bring this up because let's see. Amount support will already support units that are not in Intl. Because amount is not opinionated about the specific unit that's being used. The reason I bring up this argument is because it's counters one of the arguments later in the presentation in favor of including them. This idea that amount and Intl number format need to support the same list is just not the case at all. Amount is going to support arbitrary units that Intl does not have. Intl already throws and formatting non-sanctioned units. So even if amount supports time units, there's no expectation that Intl needs to do the same. And then six, Intl itself even is already a subset of CLDR. It's already narrowing a narrow subset of the sequence units. Therefore, excluding time units is not creating a new class of errors. So if you remember from the Intl sequence units proposal, there's a specific table of sanctioned sequence units that we allow. So for example, we allow meter and centimeter, but we don't allow meter and inch. Because there's not an integer or multiple between them. That's the decision that we made for sequence units. So it's already the set that we don't support arbitrary sequence units. So we're not creating a new class of errors like it's not like we're special casing time units. Because we're already it's already the case that we're only supporting a very clear specific subset of new units. All right. So that was the arguments in favor of position ones. + +SFC: Now let's go over the arguments in favor of position two, which is that we should include the time units in Temporal sorry, in amounts and in Intl number format. So I should be include them. Argument one is that we want to align with the CLDR. So sequence units, I mean, unit conversions are defined in terms of CLDR. CLDR ships a file called units.xml. And unit.xml contains definitions and conversions for over 200 units. And the plan with amount is to be defined based on units.xml. And units.xml includes time units. So it's weird that since we're trying to align with CLDR, like we should just use what's in units.xml. Also, there are usage preferences that use the time units. So for example, if you create an amount and you try to use a usage context in order to do a unit conversion, a usage context, for example, is person, height, or road length. And those let you it automatically picks the correct localized unit for you. There's unit context in CLDR like duration media. Which is like the length of a YouTube video, for example. And those usage preferences use time units as well. So it would be weird to not have those be in amount. The second is ergonomics. So if amount prototype convert to can produce these mixed time unit objects, then it seems like arbitrary friction if those valid amount objects can't be formatted with Intl number format. And third one is flexibility. So developers supporting arbitrary unit conversion should not need a separate code for handling time units. Of the arguments in favor of position two, this is the one I find most compelling. Like if I'm writing code, and I'm writing code that does general purpose unit conversion, and then like I can handle all the units, but not time units, then I need to do like if unit is time unit use duration, else use amount. That's a little bit weird. And that reduces some friction. And then while duration format is ideal for dedicated duration handling, it should not be the only permitted approach enforced by our artificial restrictions. And then number four is not a formatting hazard. So what does this mean? So this means that if you do try to format a duration sequence unit with Intl even though it's not as powerful, the formatting is not as powerful. It's still works. It's still fine. It's still like correct I18N. And like if you want more if you want more knobs, then you can go to duration format. But like the default behavior in Intl is not wrong. It'll just format, for example, five minutes, comma, 30 seconds. And like you can't configure that further. And if you want to have more knobs, you just use the existing duration format. + +SFC: All right. So these are some of the questions that I had for discussion today. So basically, how do we deal with time units in both Intl sequence and amount? And these are some of the questions, for example, of call sites and like what the behavior should be for each one. And I wanted to sort of hear some from other delegates because we already discussed this in TG2. I wanted to seek if there's any other perspectives that we were missing and hope guide us to a direction on this. Let's see. Got a couple topics in the queue. Why don't EAO, why don't you go ahead and go ahead and do your topic first? + +EAO: One new question that came to my mind as you were talking about this. Is the consideration of how, if we end up in a world where amount supports time sequence units, but Intl number format does not, and if I do have then an amount with time sequence units, is your expectation then that `Intl.DurationFormat` would be the tool that I could use to format that? + +SFC: I think that's an interesting—so your question is could you format an amount with a Intl duration format? And my answer to that would be I don't see why not. + +EAO: Okay. Because this then leads to asking if I have such an amount and I call its `.toLocaleString` method. Is your expectation then in this scenario that the options bag there would be a duration format options bag when it is a time-based amount and a number format options bag when it is something else? + +SFC: It's an interesting question. I think that it's one that we would need to discuss further. I'd expect that there would be some of the friction about this point three, which is that like if I'm doing like if I'm writing a generic amount code for unit conversion, I still need to have like a if time unit is sorry, if unit is time, then do this shape of options bag because the options bag shapes are definitely not the same at all. So there's still a little bit of friction if the two locale string has different shapes depending on the shape of the options. So that's a discussion that's a question we would need to discuss. But like I don't see anything philosophically wrong with that. It's just a question we need to discuss in terms of developer ergonomics and expectations. + +EAO: So the reason I'm asking this is to lead to pointing that I believe that you are presenting that there needs to be some level of a link in the discussions on what we end up supporting an `Intl.NumberFormat` and what we end up supporting in amount because these relate to each other. And I mean, on that topic, my overall position is that `Intl.NumberFormat` and amount should both be completely fine with the time units that we already support formatting in `Intl.NumberFormat`. In particular, as I'm relatively certain that the envisioned `Intl.NumberFormat` sequence unit formatting is for time units a strict subset of the functionality provided by `Intl.DurationFormat`. And therefore, should pose no conflict if we just supported them. + +SFC: All right. I don't see any other topics on that one. So let's go ahead to PFC's topic. + +PFC: I'd like to point out that the design of the `Temporal.Duration` type involves many choices about how to convert duration units into other duration units. APIs could make different choices about that and those choices would also be valid depending on the use case that you want. We made certain choices in JavaScript based on what we think the use cases are for users of JavaScript. But for example, we saw in looking at analogous APIs in like CalConnect, that they made different choices. I don't know if the Unicode stuff involves different choices about duration units, but I would really like to avoid that we have two facilities for handling duration units in JavaScript that make different choices. So the kind of choices I'm talking about are like, are you allowed to convert between seconds and minutes or not because there might be a leap second, or are you allowed to convert between hours and days or not because it might be a DST day. I don't really have a strong position on whether Amount should support the duration units. I do have a strong position on not ending up with two things that make different choices about duration units. + +SFC: Thank you, PFC. You have a reply, EAO. + +EAO: One thing to note that the envisioned spec for `Intl.NumberFormat` in particular does not include any conversions of any sort in that it is structured in a way that we are just formatting what the user is directly asking for. And seconds never become minutes. + +SFC: All right. I think the next topic on the queue would be WH. + +WH: I find the arguments for position 2 (include time in Intl number formats) to be convincing. I don't want the arbitrary friction of the use of Amount with time units being different from the use of Amount with a mixture of time and distance units. That's just arbitrary friction for users that doesn't accomplish anything. A lot of things that Temporal solves are non-issues in Amount: In Amount you never add a duration to a time to get another time which is where leap hours and such come in—those just do not appear in Amount at all. + +SFC: All right. The next if the chair is going to advance the queue, it looks like next on the queue would be RGN's topic. + +RGN: Yeah. If you could go back to your questions slide. Regarding question one here. You know, position 2 I believe was put forth by me but just to get it on the record here. Amount should definitely support single time units. It would be utterly bizarre for them to have a special exception in a protocol and API that is otherwise completely unopinionated. You can use arbitrary well-formed strings as units with the current design of the proposal. So there's no reason that there would be a special rejection of time units. Similarly, and extending that, it should support conversion per CLDR like we just talked about with WH… hours between hours, minutes, seconds, etc. CLDR also supports conversions that don't exist even in Temporal, like years to quarters and vice versa. These conversions are well understood and are not really taking place in a Temporal domain. They're taking place in a generic Amount domain. These are conversions that can happen just by virtue of our understanding of what the units are, without having applied any special cases to them. And it's useful. You would want to be able to have a generic program operate on units generically. I'll pause now to see if SFC has a reply before moving on to my next topic. + +SFC: Yeah. I put a reply. Yeah. I'm yeah. I put a reply to this topic so it kind of also tying in a little bit with what PFC is saying. So I think that Temporal made some decisions about which duration units are convertible without needing a reference point so that there's a relative to that you can use in Temporal duration to do like conversion between any of the units and then if you don't have that if you don't have relative to then you can only convert within a subset of the units. And Temporal made the choice to allow for example data hour without a relative to which is a little bit you know uncertain because of the whole you know 24 hour like there's not necessarily 24 hours in a day. But that's a decision that Temporal made but it does definitely does not allow days to months and months to years. So we'd need to evaluate that against what's against the decision CLDR made. The decision currently in units.xml is that you can do hours to days is okay and you can do months to years which is I didn't really agree with that when they did it but they decided to allow months to years but they don't allow days to months. So like if we wanted to align with Temporal and also support the conversions of the time unit in amount then like we would be differing a little bit from CLDR in the sense that months and years should be not convertible. So that's the main comment there. Like I think that in my opinion that's the furthest we should go is like you know supporting these in amounts but under the constraints of Temporal. That's sort of the like furthest that I think we should go in the direction of support. So that was my comment. + +RGN: I think that's plausible. Unlike the acceptance of any well-formed unit, conversion is something where you have a sanctioned list. We don't have the ability to convert arbitrary units because we don't even understand arbitrary units. So to take CLDR as a starting point and constrain it down to what Temporal supports without a reference point is reasonable. To remove conversion completely is not reasonable. + +SFC: EAO is next on the queue. + +EAO: Question SFC. Do I understand correctly that your position then is that if amount conversions are restricted by Temporal decisions, then because Temporal does not include “quarter” as a thing, that conversions between years and quarters should not be supported? + +SFC: I mean I think CLDR defines more time units it supports for example centuries and decades. Right. And fortnight and those have well-defined conversions to their base unit the way that I would define quarters is that like we'd need to have some interesting discussions on whether quarters are convertible to months or convertible to years. And once we make that choice then like quarters should just be in either the year bucket or the month bucket. You know something like decade and century is obviously in the year bucket. Fortnight is obviously in the day bucket. Is quarter in is a quarter defined as three months or is it defined as one fourth of a year? Interesting question. Right. That's a decision that would have to be made so I don't think that we wouldn't support quarters but we'd need to define what the semantics are. + +RGN: Well the semantics are already defined by units.xml in CLDR. Either we support them or not, but we wouldn't redefine them. + +SFC: Well we can't use units.xml for this case because units.xml has month defined as one twelfth of a year which is not the semantic that Temporal has defined. And I agree with Philip that we shouldn't define that semantic that's different than the Temporal defined semantic. + +RGN: I'm willing to negotiate on that later. I think this is a fine point inside of a bigger discussion. My position would definitely be that if it is supported it does not deviate from what is defined by CLDR. + +SFC: EAO's next. + +EAO: Could I ask SFC you and or PFC or you both to take the lead in looking into the CLDR units.xml to identify these specific conversions that you believe should not work the way that they are defined in units.xml? My suspicion here is that it may turn out that the only one that is between months and other units that ends up being problematic, but it would be great to get you guys to basically take the lead on identifying the problematic ones. + +SFC: Yeah. I did look into that a little bit before this presentation and posted a comment on GitHub and I agree that I believe months is the months and as an extension quarters are the main like deltas. + +PFC: Sure. I can take a look as well. + +EAO: Thank you. + +SFC: All right. I think we've finished that thread of RGN. So RGN do you have a new topic? + +RGN: Okay. Yep. Next one relates to Amount question number 2 here. Where again, just like I mentioned for the previous point, because Amount is currently on a track where it's constraining units only on well-formedness and it doesn't need to understand any associate semantics, lack of support for sequence time units as expressible in Amount instances would just be perverse. That you could have bushel-and-peck but not hour-and-minute, for instance. This does not imply conversion support. It doesn't imply any special semantics, just that because arbitrary units are supported, that should include time unit sequences. If it goes in a different direction, if special restrictions apply to sequence units that don't apply to non-sequence units, then we end up with a sanctioned list like exists in Intl and that's fine. But as long as constituent units of a sequence are allowed to be arbitrary, which is my understanding of the current state, then arbitrary-with-exceptions is a terrible spot to end up in and one that I would reject. + +SFC: All right. I've got a reply to that one. So a related topic in the amount proposal one that we haven't completely resolved at this point is like the degree to which amount should be opinionated about sequence units. So we agreed that amounts supports arbitrary units but does it support arbitrary sequence units? And there was some interesting questions especially involving how like the dot value field behaves and a lot of these questions go away if we say that amounts supports only those sequence units where the units are at least convertible from between each other. Like at the very least like if you have a new amount of a meter and a second. Meter and second are not convertible so amount should at least like there's many ways to. Restrictions but it should at least not allow that one meter and second. And if it does not allow meter and second then naturally it would not allow month and day. If month and day are not convertible but it would allow or it could allow for example day and hour. So long as day and hour are convertible. So that would be one self-consistent mental model. + +RGN: It's self-consistent only to a limited degree, like if it allows “smoot” as a single unit it has no idea whether that’s length or time or mass or whatever. And if “smoot” can participate in a sequence then this is what I'm talking about. If units whose dimension is not known can participate in sequences but units whose dimension is known might or might not be able to, then that’s a bad API. It's better to say that “meter-and-second” is a fine unit string. It's a sequence of well-formed single units that Amount doesn’t know how to do anything special with. Just like it doesn’t know how to do anything special with “bushel-and-peck”. + +EAO: Just wanted to check SFC, my understanding of your position here would be that it should not be possible for example to have a year-and-day Amount and for this to be formattable, even if we end up with a model where this the formatting of this would go through DurationFormat which does currently support for example year and day as a thing that can be formatted. + +SFC: So in the position that I just articulated before which is the one where Amount is opinionated about which sequence units that it allows then year and day would not be a valid amount. If we go with the RGN's sort of hybrid version where year and day is allowed as a string but does not have meaning outside of that, and in the world where we use Intl DurationFormat to format these then let's leave that as an open question. Let's leave EAO's question as an open question . If both of those decisions overlap then that's an interesting question to answer. + +SFC: I'm next on the queue. So I wanted to look a little bit to one of the comments Waldemar made about the output of Amount conversion. It's one of the I guess number two here position two number two which is I just wanted to sort of go a little bit more into the how people feel about the conflict between this here position number two and position number five which is that it's already established that Amount is more general than Intl number format like amount will be able to likely support bushel and Intl number format does not contain bushel until someone comes to us with a compelling argument that we need to support bushels. Intl NumberFormat is unlikely to support bushels. So it's already the case that Amount is not formattable with Intl NumberFormat. That's already the case. And given that that's already the case how does that tie in with this ergonomics thing where it's already the case if you call convertTo and you get out a bushel that's not going to work with Intl. If you call convertTo and you get out a minute and second is it really a different expectation that that would work even though the things you get out of convertTo do not necessarily all work. Like is this concern a valid concern? EAO you have a reply in the queue. + +EAO: Are you proposing that we end up in a situation where you could have an amount that is formattable but that if you apply some conversion to it and you end up with something like for example a sequence unit of whatever that this would then not necessarily be formattable. + +SFC: I'm saying I think that I think that's already the case. I mean bushel is a unit of what? Volume? A weight or volume? I forgot which. And like it's already the case that if you that like if there's some CLDR units that there's already a unit preference or you can just directly ask for that if you start with what would we say? It's a unit of volume I think. Yeah. So if you already start with like. + +EAO: I think bushel is supported as a CLDR units.xml unit but I take your point that there may indeed be pairs where we support the formatting of a subset of the units in a single category that is internally convertible. + +SFC: It's definitely the case. Yeah. One example is even in like distance there's a lot of distance units that are not in Intl. There's a lot of for example like area units for example. Waldemar go ahead. + +WH: I don't understand this hypothetical. Are we talking about a hypothetical situation in which Intl does not understand what an hour or a second or a year or a month is? + +SFC: So Intl knows what those are and it like it can format a single unit hour and minute already. This is just only about the time sequence units. Sequence units of time. Is what's on the table. + +WH: Okay. I'm not that interested in the case in which Intl doesn't understand how to use a month in that time sequence. I just don't see what the point of that hypothetical is. Intl does understand what an hour and a month is. So what are we talking about here? + +SFC: We have just a few minutes left in the time box. It's only been five people who've been talking. And one of the points of having this on the TG1 agenda was to get see how if there's any other delegates that have input. So if there's anyone besides me, WH, EAO, RGN, and PFC who have thoughts on this I'd really appreciate if you could enter the queue. + +JRL: Or they could speak directly. They don't want to enter the queue. + +SFC: Yeah. + +JRL: We do only have a little over a minute left though. + +SFC: I'll also go ahead and if you don't feel comfortable speaking now and you're still formulating your thoughts on this topic you can always contribute on GitHub and these are two of the links you can use. This is the one for Intl sequence units and this is the one for proposal amounts. + +SFC: I'll go ahead and start dictating a conclusion. + +### Speaker's Summary of Key Points + +* We heard input from PFC as well as from WH. That adds to the input that we had already received from SFC, RGN, and EAO. +* Constraints of Temporal could come into conflict with the constraints from CLDR. +* Overall the question feels largely unresolved but I feel that there is room for having a compromised hybrid type of solution that could address some of the concerns from both position one and position two. +* The other large direction that needs to be investigated is the degree to which amounts should automatically pick between an Intl duration format and an Intl NumberFormat when formatting an Amount. And that's a new suggestion that arose from this discussion that the champion should also go and investigate further. + +### Conclusion + +Continue iterating on the proposal, taking the new perspectives into consideration. + +## Linear Matching + +Presenter: Michael Ficarra (MF) + +* [proposal](https://github.com/michaelficarra/proposal-linear-matching) +* [slides](https://docs.google.com/presentation/d/19Y18Lt165cSwqLI7K7aeA6IbSp9FOC1psrC2_MfaLs4/edit) + +MF: Linear Matching for stage one. We presented much of this content at the previous meeting. I'm presenting this on behalf of our champion group that also includes AUR and CPC, who are both here and will be able to participate in our discussion. So the motivation for this proposal—this is going for stage one. The motivation for this proposal is that the regular expression denial of service is a vulnerability that is incredibly common today. CVEs are issued for it constantly. It's very, very easy to accidentally use regular expressions in a way that causes them to be evaluated in greater than linear time. It's also tricky to detect these because they may not be triggered until you receive a specific kind of input which may come from end users, whether that is a benign end user or a malicious one. This is a particularly bad kind of issue because the program does not just throw or something. It enters an unrecoverable error state. It just hangs for effectively forever. This is a really, really bad kind of issue. There are linting plugins that can somewhat detect this. They're not exactly reliable because the spec doesn't actually make any performance guarantees. There are different engines that may do things differently. So they're not really an actual solution to this. They should be used in the absence of this but still aren't an alternative. And if we ask users to do this via a library or something, this library would both be very large to ship for anyone who wants to use regular expressions, a very common feature, and also much, much slower than a native implementation. + +MF: So I just want to talk about some use cases. These are effectively user stories. So we often have programmers that want to take some kind of input and produce a regular expression from it. This happens both in that programmer's code where they explicitly just create a regex from some kind of generated string or in libraries where there's globbing libraries or something where they pass in something that changes in different executions of the program and can produce regexes that sometimes have this behavior. + +MF: It's not just creating the pattern from unexpected inputs but also passing unexpected inputs to an existing pattern which can lead to surprising behavior. I said that testing these regular expressions leads to an unrecoverable error. Well, it would be nice if the programmer could specify a way to recover from this state, whether it be from timeout or resource exhaustion or something. There's plenty of times when you can give a reasonable fallback and there's just no way to write that today. Also often the person that is writing the pattern is not the person that's testing the pattern. So we should be able to communicate the safety of this operation, right? Right now you just pass a regex to somebody and they don't know whether it's safe to test, or on what kinds of inputs it'd be safe to test. So we need to consider that case as well. + +MF: And I've been thinking about and talking about this. We've come up with a couple of different components of what could be the solution here. This is not going for stage two. So we're not looking for agreement that we are going to go with any of these, but we're demonstrating that this is not a dead end. There are ways to solve this. And we want to just run through a couple of options we have here. + +MF: So one thing we could do is that we could have some indicator that—you could ask a regex whether the engine can match this regex in linear time. And this would be a way to expose those engine differences that I was referring to before that linters wouldn't be able to statically differentiate. + +MF: Another thing we can do is have an `exec` variant, or variants of the various regex methods that have guaranteed linear time evaluation. And if the regex is unable to be evaluated in linear time, again allows you to specify fallback behavior, maybe via throwing. We can also say that when constructing a regex, we want to say that all uses of the regex test methods will be expected to be evaluated linearly, and if you're not able to do that, fail to even construct the regex. And we can do that via something like a flag on the regex literal. And I refer to this as well as a possible solution. + +MF: We can pass second parameters to these fundamental regex methods that indicate some kind of timeout or resource limit after which the backtracking implementation could fall back to a linear implementation. Because often it's not always better to just use the linear implementation. You still want to use the backtracking implementation sometimes because they will commonly be faster. But when the engine realizes as it's evaluating it that it's not making progress or not making progress as efficiently as the programmer wanted, it can be asked to fall back to the linear implementation instead which will, in practice for that regex and that input, possibly be faster. + +MF: I also want to think about certain regex features that we know should be very easy for an engine to implement in linear time. So maybe we can make perf guarantees and then the things that the static checks are doing could be stronger so that they can actually tell you when you're using something that's outside of the guaranteed linearity set. Unfortunately, I think we would have to be very conservative here because this is still a field that is actively being researched. As we talked about last time there are certain features that until recently weren't even known how you could implement them linearly. We wouldn't want to require that kind of thing because that is just pushing everybody up against this bleeding edge and really quite a burden. But for many features I think it would be reasonable to be able to provide those guarantees. And spec perf guarantees are not a new thing. We do similar things with sets and maps and stuff. + +MF: I want to briefly cover prior art. A lot of other languages have the ability to do linear matches. There's different strategies that languages take. So this is like a selection of them based on the different ways they do this. So some of them just don't include features that they don't know how to implement linearly. Some of them, like Ruby, use this memoization strategy. There is a library called RE2 that can effectively I think generate implementations of a regex engine in different other languages. .NET passes a flag that disables backtracking and also disables features that would use it. Yeah, and Golang has a regex that just omits the features. There are also JavaScript libraries—as I said RE2 generates an implementation for Node. And then there's incomplete implementations that I was mentioning before that are kind of a burden to ship and won't ever be anywhere near as performant as a native implementation. + +MF: There's also been some experimentation on this in engines. V8 has an experimental engine that is not complete, it's not fully up to date with the language, and I don't think it is actively maintained, but there is a flag where you can enable a regex flag which is like one of the solution spaces that I had covered earlier. They have an `l` flag. I don't remember the exact semantics of it, and I'm not going to guess. + +MF: So here's my problem statement for stage one. The problem is that there is currently no built-in way for programmers to match regular expressions without the risk of catastrophic, unrecoverable failure. We should do something about that. And I would like stage one. So we can go to the queue. + +Okay. First up we have KG who's still typing. + +KG: Yes. I am in support of this. I do feel kind of weird about proposals which would expose engine differences. Like I'm uncomfortable just saying ask the engine if it can do this. If that's a thing which would be different across engines. But certainly that's not something we need to nail down right now. That's just like I wanted to mention that since you brought that option up. But I support this for stage one. + +OFR: I think that's a good point and it's also something that I struggle with the current version at least all the possible solutions. And so I'm not sure it might actually be something that we should think about first before advancing because if we don't see any way of making it predictable then that's kind of yeah that is actually a big problem I think. So I think it would be quite problematic if—it's not even an engine difference in that sense because it might even change between versions and it's not like it's not like basically any update could change this the response whether a regex can be matched in linear time or not. And I think that would be a pretty bad situation. So if we don't see a way of designing around that problem then I think the proposal really has a problem. + +MF: And you say that would be a pretty bad situation. Why is that? If what they're doing is a test whether they can execute linearly and then either running the regex or having fallback wouldn't they just go into the fallback case, which is a normal expected behavior on other browsers or different versions of your browser? + +OFR: I guess the question is a bit: would it be useful then if you can never rely on getting the linear case and also like a completely compliant engine could just always return false, right? So. + +KG: Yes. Although I want to respond to MF first. I don't think you can reasonably assume they will have a fallback. I like to put asserts in my code. I was doing this literally this morning. I added an assertion to some SQL code that it didn't do a full database scan because I want to find out with an error if it does a full database scan for that statement. But it's not like I have a fallback. It's just an `assert`. And I would absolutely expect people to start doing this as well. And the thing that would then happen if the engine updated and this became non-linear is that their page would stop working. Which, maybe that's the thing that the page wants, but that doesn't mean that the engine is going to be okay shipping that change and breaking the page. So I don't think that's a reasonable assumption. + +KG: Also, OFR was saying this is maybe a problem for the proposal because if we can't find a way to do this deterministically but I think several of the things MF proposed wouldn't rely on exposing engine details. There's definitely ways that we could do something in this space that would have the same behavior for any implementation. + +KM: I agree. I think developers are going to expect that once something worked and it deployed it's never going to like to unship that or change in any way. I've seen that with so many other features that I just highly doubt I would ever not see that. But that's possible here. Once it becomes linear it can never be not linear. Would be the answer. Or you just have to lie and actually ship something that's not linear and pretend that it is linear. + +CPC: Just reacting to this question across browsers. I agree that it would be odd to have something that exposes perhaps an internal detail of the browser as will you do this linearly and maybe other browsers and will you do it now but maybe you'll change your mind in a week or something. But we could imagine once we start focusing on actual solutions at a later stage to say we will specify a subset of the regex language that must be supported linearly. So that you would not be having a guess at what the browser supports currently and will support in the future but instead the standard could say: for that subset linear support must be provided. And at that point you have uniformity across the browsers once they implement that part of the standard. + +OFR: I just reread the prompt statement and I think it's totally fine with this problem statement. So I think we're good. + +RGN: One of the things I'm wondering about is whether linear time is actually the right cutoff as opposed to similar adjacent time complexities. Such as quasi-linear maybe even just subquadratic. Like there's a very rich space here looking at the prior art slides. Are all of those literally linear or are some of them just faster than quadratic or faster than exponential? + +MF: Off the top of my head I'm not 100% sure but I believe they are literally linear. My co-champions might be able to answer more detail. + +AUR: Most of these completely linear in the size of the string but most of these are typically not linear in the size of the regex. And also many of these say they are linear engine but are not but are quadratic in the size of the string in case you are doing natural rather than match. So you're right that there is a diverse ways to interpret linear term. + +RGN: Okay. Yeah. Once we advance into actual proposed solutions I wouldn't want to mislead in that way. But if they are literally linear then that's fantastic. + +MF: Yeah. And given our problem statement I think we would consider it a success even if we do not achieve exactly linearity. If we just make subquadratic guarantees or something like that it would still satisfy the problem statement. + +RGN: Yeah. Actually I guess just jump ahead and delete my future topic. I absolutely approve of stage one with this problem statement. It's very well crafted and works for me. + +WH: We should be careful what we're quantifying over in the discussion. I've been assuming that we're talking about time linear in the length of the input string. However, you can have a regular expression engine that is linear in the length of the input string but, for example, exponential in the length of the regex. And so linear does not always mean fast if the regex is user-specified. + +SFC: Yeah. Just to note that the Rust regex crate which is linear has several paragraphs in the top level documentation page explaining what it means when it says linear. And whenever it says linear time it puts it in quotation marks and it defines it as being the linear in the product of the length of the expanded regex and the length of the string. And it also is very clear that when we say linear we don't necessarily mean fast. We just mean linear. And like it could be linear with a large constant factor but as long as it's not exponential. Right. And I feel like that is the same vibe that MF is going for here. Like you don't want to have a regex that can cause your program to get into an essentially infinite unrecoverable loop. And that's the guarantee that at least the Rust regex crate which serves over 90% of use cases in the Rust ecosystem that's the constraint that they've been using. + +AUR (on queue): in reply to Shane: yes but that statement from Rust does not apply to counted quantifiers r{n,m}. Also does not apply to match_all. + +OFR: Since you mentioned our linear engine and there was a bit of a question regarding the status I just wanted to expand a bit on that. So we do have this linear engine and it was added to the code base some time ago. And the thing is I don't so there is no plan to ship it I would say. And the reason for that is that there is a distinction between the worst case matching time and that's what we're talking about here. And there is also the average case time to match a regex and that's actually what's important for how fast the web feels. And so for us it definitely going to be like basically the more important the more important property in almost all of the cases is going to be the average time. And that's always going to be faster, to the best of our understanding with our two implementations, that's always going to be better with the backtracking implementation. And so effectively this kind of limits the design space of this proposal a bit and in two ways. Like one way where it limits the design space is that we cannot just use linear by default or add any kind of mechanism that would push users towards using linear by default. It always has to be an opt-in feature because it's only reasonable when you're actually worried about this exponential time blowup. And the other limit is basically that at least for us I think the only way to implement this proposal would be to effectively ship two completely separate regex engines in V8. And I'm not saying that's off the table but I'm also saying that this might be something that we say that is just too much given the use case of it. But I mean it really like I don't have a finished opinion on it but I just want to make this caveat here. Like the implementation burden for supporting this L flag for example would be quite high I think. + +MF: Yeah. I agree with you and I wanted to make sure everyone does understand that we do still expect the backtracking implementation to be faster most of the time. That's why, in our explored solutions, I personally lean toward the ones that use the backtracking implementation until some resource exhaustion and then fall back to the linear implementation. + +KM: I agree with OFR about having a second implementation. I think also one problem though with the bail out and then going back to the linear thing is that like we know for example that like the tier up counters for deciding when to go to the next optimization tier impose pretty large overhead on code. I think upwards of sometimes up to 5 to 10% performance overhead of just counting to figure out should I try to optimize this further. So that count might lower the average for the regular expressions which in and of itself might be unacceptable. In the average case because it's probably very rare for regexes to actually care about that. It's possible you could pass some flag that says to do this and then bail out to the regex engine. Which I guess leads to the second part, which is, what's the adoption story here which I think you kind of covered a little bit. But I guess I want to double-check. I'm assuming we're not going to—I think a lot of developers when they reach for a regular expression they don't necessarily understand the nuances of how regular expressions work; like all of the memes online are like about some crazy regex that nobody understands but does something, they just kind of grab it and use it with understanding it. Is the story we're going to try to tell that you should always try this one first? I would assume not based on our discussion but then we probably need some other description of how we're going to recommend on MDN or somewhere else, when to use this, why it's useful. I think there's a risk that people use tools that are not appropriate and we create more confusion by adding more of them. And is that worse than the cases that need a linear time regex today but you get the backtracking ones and have problems because of it. + +CPC: So I agree that we don't have an entirely complete story on what the adoption path will look like. Personally I think there's something slightly scary if we say most of the user base does not understand the nuances and we have been providing them a default that isn't safe. And it does go faster on average but it is quite unsafe. And so I think at least we should have the safe version available. Then there would indeed be a valuable discussion around how do we push people perhaps towards the safer option. It's a bit unfortunate to be in a world where the default option is the unsafe one. I think one thing that's encouraging on that front is the fact that in the Rust ecosystem, despite the fact that there exists backtracking implementations that are on average faster I have seen very few large Rust libraries reach out for those as opposed to the de facto default one in the community which is Rust Regex and is linear. + +AUR: Yeah. I was just saying that I wanted to reply to OFR that of course shipping two engines is a lot of work and that makes a lot of sense. There are some algorithms that you can just slightly modify a backtracking engine in order to get linear time complexity. It has bad memory usage but it would still satisfy this requirement. So that could be one way forward. I'm not saying of course that it is a way forward but maybe that's something to consider. + +MF: I believe that was the strategy that Ruby uses. + +OFR: Clarifying question. So is it basically like—could you just rewrite the regex basically in the front end or what are you referring to? + +AUR: Not really in the front end. It's more like you're going to save which parts of the regex you have already explored for each input position. And so if you end up in a configuration that you have seen before you can already say to the engine okay please terminate. So this means you have to write a bit more to memory of all of these configurations. You have already explored but it could be turned on or off depending on whether or not you are looking for this linearity or not. And you have to do it in a bit different way specifically for ECMAScript semantics rather than in other languages but it is still possible. Does that answer the question? + +OFR: Okay. Yeah. I guess sort of. I mean just maybe as a bit of a background like basically the regex engine in all of the engines are basically JIT compilers themselves that optimizations that have heuristics that so I cannot immediately know if this intuition maps to it and if we would even have anywhere to store some kind of side data there. But yeah. Okay. Makes sense. + +KG: Oh. Okay. Yes. Just a question. Is a match with timeout in scope? Like that does solve a number of these problems if you can just call `.match` but be guaranteed it never takes more than you know one second. Would that be something you would consider in scope for this proposal? + +MF: Yeah. That's what's on the screen right now. That's what I presented as part four. + +KG: I guess I was distracted by taking the notes. Okay. Thank you. + +CPC: Just a quick reaction to that. So yes it's in scope. There's a subtlety as to whether the fallback behavior once you run out is to fall back to a linear engine or whether the fallback behavior is just to throw an exception and say "out of resources". Of course throwing and being out of resources is safer but it still doesn't give you quite as much flexibility right because you are now having to recover from the exception. Your regex didn't work. What do you do next? + +KM: This kind of goes back to something I was saying before—you're doing back edge counting, that incurs a pretty large overhead and so that might not even be possible for the generic case. It's possible you could like you wouldn't have to have a second engine but like you're everything will run slower in that mode. Whether that's that I'm not saying that's necessarily a dealbreaker. I'm just saying that the if it ran all then I think a timeout runs into all sorts of weird problems like anything measuring time gets nebulous because like what does that mean and like what happens? There's no like guaranteed time like if like you know the operating system doesn't schedule your process then like what is are you counting wall time or are you counting CPU time? What are you counting? And then specifying that seems really hard. So I'm not sure that that necessarily works either. + +MF: I just want to say that if we went with an API that's like what's on screen, these would be people who have asked for it. I wouldn't say you're making everyone slower. You're making everyone who's asked to use it slower. Which seems reasonable. + +OFR: I think some engines do that by default, not? /eom + +CPC: Yeah. There was just a separate answer to do we want to have these fuzzy timeouts that are based on time? Saying there's precedent in the ecosystem. For having timeouts based on wall clock time. In many of the APIs of the web. + +KG: Yeah. Just seconding that and also like for the case that I care about this for it's just I'm basically protecting against attackers and I don't really need to have a fallback and I don't really care if the timeout is precise, I just need it to not blow up. Like that's the only guarantee I want. + +KM: Yeah. It's understood that first of all I guess setTimeout is not part of JavaScript spec itself. That's part of WHATWG or W3C so it's not part of the core specification. I mean basically every runtime implements it but it does not mean it's part of our thing. We don't have any concept of time elapsing in the JavaScript spec. So right now as far as I know it's also setTimeout is a lower bound on the time. It does not have an upper bound. It doesn't say this must happen within this time frame, it says something will happen after some time frame which is a bit different. But I agree that yes the goal here is to prevent total exhaustion which is probably fine. It just might mean that like it is a "please do this" specification without any real normative effect because there is no way to actually affect the outcome that we are mandating. + +MF: `Atomics.waitAsync` exists. Which also takes a timeout that we have now specced. + +JRL: We have a bunch of plus ones on the queue. Then LVU. + +LVU: Okay. I was wondering if it might since we're throwing ideas out I was wondering if it might make sense to have like a generator version of exec that would yield once it's done "enough work" and what enough work would be would largely be implementation dependent. Then it's up to the author to decide what that means for their use case whether they want to use a time measurement or some other criteria, whether they want to take the regex size into account or the input size into account but the API doesn't have to encode anything. It just needs to yield. And that is also to the point that was just mentioned around. I just don't want it to blow up, I don't care about the precise time, and I think that most use cases don't care about the precise time. They just don't want it to be too long. But also as I've as I was also in the queue to say I would support stage one because at this point it's not about the particular solution. + +MF: Yeah. I think that's a good idea and we'll include it in the solution space that we look into during stage one. + +WH: I'd like to reiterate that linear time does not mean that it doesn't blow up. We're talking about linear time in the input string. It might be exponential time in the regular expression. If you are letting users pass in arbitrary regular expressions to a “linear time engine” that’s only linear in the length of the input string, it might still blow up. So you might still want the timeout. + +CPC: Or we could specify linearity in the regex as well right? + +WH: That's impossible. + +CPC: Why? + +WH: It's impossible to have something which is linear in both and supports commonly used features such as counted quantifiers. + +CPC: I think that's what Rust regex implements right? + +WH: No. + +CPC: I believe the v8 engine also does. + +WH: There are interesting corner cases and caveats there. If you read the caveats, you’ll find that Rust’s engine is not actually linear in the number of characters in the regex. + +CPC: Okay. Maybe something worth thinking about as we approach stage two then. + +LVU: For the generator version at least that depends on when it yields. Which is TBD. I think it could maybe cover both cases. + +JRL: Okay. So up next we have a bunch of plus ones. But before we get to that I'm assuming that MF you're going to ask for stage one. \[MF shows "Stage 1?" slide] Excellent. So for the affirms: we have JHD plus one plus one for stage one would love to see stage two with a flag and an exec timeout. DJM saying plus one for stage one. Chip saying plus one to stage one, the problem statement is persuasive. PFC with plus one. SFC with plus one, thanks for the well phrased problem statement. LVU supports stage one, unsure about the right solution. CDA supports +1. MM supports +1. WH supports +1. So a lot of agreement here. + +MM: I want to take a moment for the but on my supportive stage one. I do support stage one because all of my objections are not stage one blockers. But the whole direction that we spent a lot of time on things like timeouts, I object to any solution to this that introduces dynamic non-determinism. Having a subset that is guaranteed linear and having the thing differ statically among engines or even among versions of the same engine is static non-determinism. It's not dynamic non-determinism. Timeouts or anything like timeouts is dynamic non-determinism. And dynamic non-determinism whenever we introduce that we introduce a whole set of other problems that enable side channels and we've done a lot of work in Hardened JavaScript to quarantine sources of dynamic non-determinism so that you can create constrained execution environments in which you cannot measure duration. And therefore you cannot read timing-based side channels. + +MF: Is it not possible for you to do similar things with overriding this API to have no timeout or something? + +MM: I don't think timeout improves this API in any case. If there is a compelling reason to have timeout be the solution to these problems then it would be worth having an extended conversation. But I just don't find the timeout idea (a) compelling and (b) introducing it does create all these other problems. And it's only worth negotiating complex solutions for the others to those problems if timeout is a substantial added benefit to this API which it is not. + +CPC: MM, can I ask a brief clarification question? Prefacing this with, I'm not a fan of timeouts either. But would you consider it dynamic non-determinism if you had an engine that attempts to run a backtracking algorithm for some time and then falls back, maybe not even user configurable time, but if you pass the "I want this to be safe" flag then it might decide to run the backtracking engine just in case it gets lucky for a little while and then falls back to a known safe implementation that is linear. Would you consider that to be dynamic non-determinism? + +MM: What is observable in the absence of measuring duration? + +CPC: Only matches or does not match. So it would not differ from calling the linear from the start in this context. Without time. + +MM: I'm sorry. So for the same inputs would and the same version of the same engine would it sometimes match and sometimes not match? + +CPC: It would not. And so I think I understand your objection better now. So it would not. What it would do is, it's essentially an optimization that says let me try for a little bit to see if my backtracking engine is fast enough for this and if it's not it falls back to the linear engine and gives you a deterministic answer every time. + +MM: It gives you the same deterministic answer? + +CPC: That's right. + +MM: Okay. Yes. That's perfect. + +JRL: Okay. WH. + +WH: Supporting stage one. + +JRL: All right. So that gives us I think nine people who are supporting plus one. Are there any objections to stage one? Okay. I don't hear anything. So congratulations. + +MF: Thank you. + +### Speaker's Summary of Key Points + +* the failure scenario of regular expressions today is unacceptably bad +* it's really common to run into this and there's no good existing solution +* we should give programmers a way to use regular expressions while avoiding this failure scenario + +### Conclusion + +* Stage 1 + +## `Intl.DateTimeFormat` Alignment With Other Standards + +Presenter: Eemeli Aro (EAO) + +* [proposal](https://github.com/eemeli/proposal-intl-datetime-alignment) +* [slides](https://docs.google.com/presentation/d/1JAb9AFDNdgrcDMS24iTZPTqi5p3uzHLr8JDZOI-5CYM/edit) + +EAO: Excellent. `Intl.DateTimeFormat` alignment with other standards is a new proposal looking to advance to stage one here. This is something that came up during the TG2 call we had immediately before the deadline so this has TG2 support for proceeding, and it's something coming out of some discussions we already having with folks at W3C and at Unicode. + +The motivation or the problem statement I'm going to read this out that we'd like to be solving for here is that active work is happening beyond TC39 in the web space for designing API shapes for datetime formatting. Users of the web would benefit from the same formatting options being available in HTML, JavaScript, and Unicode MessageFormat, as this would make it easier for developers to make use of them. If the JavaScript datetime formatting API can be extended to provide a solution that also works for other parts of the web stack, the same API can be available for all users. + +What we have then right now in `Intl.DateTimeFormat` in the constructor, we have effectively three categories of options that are available to be used. We have the locale and matching options, which are available whenever you are calling this. And then we have two separate sorts of buckets. We have the style shortcuts and the datetime component options. And these buckets are not compatible with each other. So if you use something from one of these categories, you can't use anything from the other category. When you're formatting, this is an existing state of affairs that we have now. And this has been the case for a while now. + +Now, the thing that has happened recently is that during the web engines hackfest, LCA presented and is championing a proposal for HTML to introduce localized time formatting without JavaScript. The idea that that proposal is working towards is that HTML already has a `