01:23 | <ljharb> | Object.is is the only equality that makes any sense intuitively. the rest are just "it matches <legacy baggage>" |
01:28 | <bakkot> | you say this but in fact I am pretty sure very nearly everyone would be surprised by a notion of equality in which 0 ≠ -0 |
01:28 | <bakkot> | so, no, I think you just straightforwardly wrong about that unless your claim is about only your own intuition |
01:28 | <bakkot> | in which case fine whatever but we should not design the language around your intuition |
02:10 | <ljharb> | i think everyone's assumption there is because of == and === and SameValueZero 's behavior. -0 and 0 are obviously different things, so without that baggage (or IEEE's), i claim everyone would assume they're different. |
02:10 | <ljharb> | tbf, without that baggage there'd be no -0 to begin with |
02:26 | <bakkot> | the expectation isn't because of 0 === -0 , it's because they expect the result of negating zero to be equal to zero, because that's how it always works |
02:26 | <bakkot> | IEEE requires that 0 === -0 as a consequence of that, not the other way around |
02:26 | <bakkot> | the result is that almost no one needs to know about -0 because it behaves identically to 0 except in a few very obscure circumstances |
02:27 | <bakkot> | if you didn't know that -0 was a special value, and you should not have to know that -0 is a special value unless you are one of the people who cares about those very obscure circumstances, then you would assume that 0 === -0 because you would assume that negating 0 didn't do anything the same way 0 === +0 , which assumption is very close to being correct |
02:46 | <Michael Ficarra> | I would counter that only people who are working with floats should have to think about (and make special accommodations for) the weirdness of floats. People writing generic libraries or using foundational built-in data structures should be able to reason algebraically. It's not worth abandoning that to smooth out the sharp edges of floats for people who arguably will need to learn about them eventually anyway. |
02:54 | <bakkot> | there are a lot more people who are working with numbers in javascript than there are people writing generic libraries which need to distinguish between -0 and 0 |
02:55 | <bakkot> | people who are using foundational built-in data structures can reason algebraically as long as they don't care about the difference between -0 and 0 |
02:55 | <bakkot> | people who do care about that difference have to care anyway |
02:57 | <bakkot> | in practice what happens is that the people writing generic libraries or using these data structures do not distinguish between 0 and -0 and this works out for everyone |
02:58 | <bakkot> | I suspect you could count on one hand the total number of people, ever, who both a.) had a case where the difference between 0 and -0 was important to them for reasons other than idealogical purity and b.) were surprised that Sets do not consider those distinct values |
02:58 | <bakkot> | possibly on zero hands |
03:12 | <justingrant> | Negative zero hands! Which is very different, of course. |
04:44 | <jschoi> | For what it’s worth, I checked today with a friend in game dev—some who works on physic engines as their job, mostly within Unreal Engine’s C++/Blueprint APIs, for the past decade. He routinely works with floating points in his physics work. He did not know what negative zero was, and he doubted any of his colleagues knew either; he said it seemed like trivia that would never affect his team and that hopefully never need to care about. (He seems to deal with trigonometric discontinuities in other ways before ever encountering a negative zero. Or maybe Unreal has abstracted it away enough that they’ve never needed to care.) He said he would find it quite annoying if he had to care when a zero is positive or negative while using it as a key in an Unreal TMap container. But he would never use a float as a key anyway and instead would a strict integer type for keys instead. We are not so lucky in JavaScript... This of course was a mere convenience sample. Take it for what it’s worth. |
04:46 | <jschoi> | Regarding Kevin’s gamedev example and game developers’ domain knowledge of -0: I checked today with a friend in game dev—some who works on physics engines as their job, mostly within Unreal Engine’s C++/Blueprint APIs, for the past decade. He routinely works with data structures containing floating points in his physics work. He did not know what negative zero was, and he doubted any of his colleagues knew either; he said it seemed like trivia that would never affect his team and that would never need to care about. (He seems to deal with mathematical discontinuities in other ways, before ever encountering a negative zero. Or maybe Unreal has abstracted it away enough that they’ve never needed to care.) He said he would find it quite annoying if he had to care when a zero is positive or negative while using it as a key in an Unreal TMap container. But he would never use a float as a key anyway and instead would use a strict integer type for keys instead. We are not so lucky in JavaScript... This of course was a mere convenience sample. Take it for what it’s worth. |
13:54 | <ryzokuken> | @room starting in 5 minutes |
14:26 | <Michael Ficarra> | hang on, can't we store the pre-transaction state in locals and roll it all back in the catch? |
14:26 | <Michael Ficarra> | is the issue that we may partially roll back because of another OOM? |
14:27 | <Mathieu Hofman> | Yes in this simple case, but it's not always straightforward to store all state to restore it |
14:28 | <Mathieu Hofman> | The point was that it's not always obvious where OOM can happen, and an undue burden to be defensive about them |
14:30 | <naugtur> | The combinations of backup values would grow incredibly fast in real programs' complexity |
14:30 | <littledan> | I imagine Chrome filed that WHATWG issue because it does implement OOM-fails-fast |
14:31 | <shu> | whether an OOM is thrown depends on the API |
14:32 | <littledan> | oh right |
14:32 | <shu> | like, if you try to make a string that's too big, that probably throws a RangeError even if there's no explicit RangeError check (like in ArrayBuffer allocation) |
14:32 | <shu> | but almost all "small" allocation OOMs crash |
14:32 | <shu> | some of it has been added previously because people repeatedly ask |
14:33 | <littledan> | you mean, cases where it throws rather than crashing? |
14:33 | <littledan> | would it be possible/desirable to implement the original OOM-fails-fast guarantee? Do we have evidence of web-incompatibility? |
14:33 | <shu> | yeah, where they ask for it to throw so they can catch it |
14:34 | <shu> | i think making out-of-stack crash is likely incompatible |
14:34 | <shu> | i don't think it is desirable to spec original OOM-fail-fast |
14:35 | <littledan> | transcriptionist notes are excellent today |
14:35 | <Michael Ficarra> | I'm not convinced that the current host hook requirement doesn't permit never returning |
14:35 | <littledan> | thanks for clarifying, Shu |
14:36 | <shu> | to be clear i think it is useless to have a host hook, and actively harmful to ultimately expose that hook to user code (if i understood what mark's long term plans were earlier in the proposal) |
14:47 | <Michael Ficarra> | why is it any more likely to be in ecosystem code than for(;;); ? |
14:53 | <shu> | "inducing a host to panic" sounds like exploiting a bug to me |
14:53 | <shu> | there is no sanctioned way to induce a host to panic |
14:55 | <naugtur> | One strong difference in the browser between infinite loops and panic is infinite loop freezes the UI and panic would inform the user. The host hook gives the hook power to decide (or even ask the user) how to react. |
14:55 | <bakkot> | lots of people put panics in languages which have them and ~none of them write defensive infinite loops |
14:55 | <naugtur> | (we once wrote a defensive infinite loop at MetaMask. Long story) |
14:59 | <shu> | what we're doing right now does not violate the JS spec, what? |
14:59 | <kriskowal> | (In the mid-aughts, Google prefaced every JSON HTTP response with while(true) to defend against XSS. For all I know, they still do.) |
15:00 | <Andreu Botella> | I think Mark is arguing that stopping a script execution through any other means than the stack emptying because the execution finished is not legal per the JS spec |
15:02 | <iain> | Making stack overflow crash is definitely not compatible. At one point SM increased our stack limit, and we saw performance reports on games websites using some weird obfuscator that deliberately overflowed the stack as part of its attempt to make itself hard to debug, because it was taking longer to hit the limit. |
15:02 | <shu> | what i was responding to was mark referred to the current OOM behavior in browsers as "violating the spec" |
15:04 | <Michael Ficarra> | how does aborting help facilitate transactions? |
15:04 | <Michael Ficarra> | or does it not become an abort, it becomes a rollback to some previously tagged point? |
15:05 | <Mathieu Hofman> | In our case, our supervisor worker embargoes all effects from the child worker and commits them once the worker completes its operation |
15:06 | <Mathieu Hofman> | It currently uses an actual DB transaction to accomplish that, but that's an implementation choice |
15:07 | <Michael Ficarra> | ah like Golem |
15:08 | <Michael Ficarra> | (https://www.golem.cloud/) |
15:08 | <Andreu Botella> | so self.close() on dedicated workers doesn't kill the worker, it just prevents new tasks from being enqueued onto the event loop, but you could signal to the parent to kill the worker, and then call it |
15:08 | <Mathieu Hofman> | (the abortable unit is obviously the worker agent as we don't use SAB) |
15:10 | <Mathieu Hofman> | Right that's great for implementing panic . It doesn't help for OOM |
15:10 | <littledan> | Can we get someone else helping with notes? |
15:10 | <Andreu Botella> | I'll help out |
15:12 | <bakkot> | I am pretty sure { object literal } as const is all of these things Ron is saying |
15:13 | <bakkot> | TS can infer the closed domain of the keys and values just fine |
15:13 | <bakkot> | and will prevent you from adding new keys and all |
15:13 | <littledan> | see https://github.com/rbuckton/proposal-enum/issues/23 |
15:14 | <Michael Ficarra> | @bakkot I'm pretty sure Ron's banking on some of these continuation ideas actually happening though |
15:14 | <littledan> | Igalia implemented : enum as an upgraded version of this, which also does some type system magic that catches further back up |
15:15 | <littledan> | the most basic thing is that as const still requires you to do const Foo = { /* ... */ } as const; type Foo = Foo[keyof Foo] which is ugly, and then the type isn't branded at that point |
15:15 | <littledan> | so I think the TS-only alternative would be more like https://github.com/microsoft/TypeScript/pull/61414 |
15:15 | <bakkot> | oh, as enum is neat, sure |
15:15 | <shu> | sidebar comment is that is very, very risky |
15:15 | <shu> | see decorators |
15:15 | <Richard Gibson> | I think it does, though... consider typeof (len => { try { return "x".repeat(len); } catch(err) {} })(2**53 - 1) —the spec requires it to be "string" AFAICT, but actual behavior is "undefined" |
15:16 | <bakkot> | I have never found having to use keyof Foo to be that painful personally but simplifying seems good |
15:16 | <littledan> | so far, I get that the advantages are 1) self-references 2) frozenness |
15:17 | <littledan> | I'd bet that the majority of TS developers who know how to use enum do not know how to use keyof to achieve the same effect |
15:17 | <Michael Ficarra> | oh I know, plus I think pretty much all of them have no shot (maybe auto-initialising with symbols would be fine) |
15:18 | <bakkot> | sure because they can use enum instead, so it's not widely taught as a pattern, but if they did not have enum they'd learn the other pattern |
15:19 | <littledan> | maybe, idk, the idiom feels kind of "low level" to me, why should I as a developer have to do this weird TS plumbing for something which is "obvious" |
15:19 | <littledan> | but none of this relates to whether we should add a JS feature |
15:20 | <shu> | isn't Ron's whole motivation slide about TS DX? |
15:21 | <littledan> | yeah, I just mean that bakkot and I were talking about tradeoffs between : enum vs the keyof idiom, which is entirely within TS |
15:21 | <shu> | ah gotcha |
15:21 | <Steve Hicks> | This might be less surprising if it were called Composite.is ? |
15:22 | <littledan> | To these, I'd add 3) Looking like existing TS enum 4) being a syntax base for further follow-on features |
15:24 | <bakkot> |
|
15:27 | <littledan> | 3 actually may be a disadvantage, because of the transition (see set -> define fields) |
15:27 | <littledan> | why are we not going to do auto-incrementing? (not that I disagree...) |
15:27 | <bakkot> | because it is very bad and I and several delegates are dead against it for that reason |
15:28 | <littledan> | right just asking what the reason/badness is |
15:29 | <bakkot> | ah, see discussion in the vicinity of https://matrixlogs.bakkot.com/TC39_Delegates/2025-02-21#L227 |
15:29 | <littledan> | if this is a feature where TypeScript continues to have custom runtime semantics (due to disagreeing with TC39) it makes me significantly less excited about this feature |
15:29 | <bakkot> | biggest thing is that it makes it way, way too easy to have breaking changes |
15:31 | <shu> | not sure i follow, how does auto increment increase likelihood of having breaking changes? |
15:31 | <jschoi> | As an aside, I have to express gratitude over having a Matrix archive website. It’s been so invaluable over the years for situations just like this. |
15:32 | <snek> | if you put a new variant not at the end of the enum, it renumbers all the following ones |
15:32 | <shu> | are you expecting that people will be like if (val == 42) and hardcode a particular enum's integer value? |
15:34 | <shu> | but ... why is that breaking? |
15:34 | <shu> | are there users of that enum that refer to the raw integer values? |
15:34 | <snek> | no i think its more like if you have a library and two versions of it are talking to each other |
15:34 | <bakkot> | yes |
15:34 | <shu> | oh, you want two libraries with different versions in the same app |
15:34 | <shu> | got it |
15:34 | <snek> | well i personally imagine more like talking over http or smth |
15:35 | <snek> | but in any random scenario |
15:36 | <Andreu Botella> | having someone else helping with the notes would help |
15:36 | <Michael Ficarra> | also people storing enum values in JSON or databases |
15:36 | <shu> | i'm still having trouble understanding why enum auto-numbering is the footgun. is it something like, the new version of the library is otherwise completely backwards compatible, but they wanted to prepend instead of append an enum value for an orthogonal new feature, and now it's incompatible? |
15:36 | <shu> | like we deal with these problems in Chrome as well, they're all append only |
15:36 | <snek> | i mean we can talk about all the times v8 has broken abi compat from inserting variants/members into the middle of existing definitions if you want examples of how this is a footgun :P |
15:36 | <shu> | removing enum values in general is a breaking change so i assume that's out of scope here |
15:37 | <Michael Ficarra> | @shu in a completely closed world, auto-numbering would be fine, but the value of the enum escapes or is stored somewhere, so in practice it's rarely okay to re-number your enum |
15:37 | <shu> | yes, we can. i don't really know how well we deal with public API enums, probably too cavalierly |
15:37 | <littledan> | I guess the idea is, it's just too tempting, syntax-wise, to add new values into the beginning or middle |
15:38 | <shu> | okay thanks |
15:38 | <snek> | i feel like i would most enjoy the default being unique symbols for each item, unless you explicitly want the value to be serializable in which case you need to bring in your own numbers/strings (or use a decorator?). just to get a base level of mindfulness for how your enum is used. |
15:38 | <hax (HE Shi-Jun)> | In real life u might find someone made mistake. For example, in some versions of Monaco editor, I found the enum value changed in the new version and cause bugs in my application. |
15:42 | <Josh Goldberg 💖> | As a reference, in typescript-eslint's recommended configs there is a rule that bans comparing enum values with non-enum values. That's mostly targeted to stopping developers from relying on specific values of enums.
|
15:42 | <hax (HE Shi-Jun)> | I remember there is a proposal by engines try to remove TDZ anyway? |
15:42 | <bakkot> | if you maintain the discipline of append-only it's also fine but people don't; they like similar values to be close to each other in the declaration |
15:43 | <Michael Ficarra> | 🤔 there's nothing protecting a user from giving more than 1 enum case the same associated value? that seems bad... |
15:43 | <shu> | yeah i understand that. there are many warnings about APPEND ONLY DO NOT REMOVE ANYTHING etc |
15:43 | <Michael Ficarra> | also just alphabetical so they're easier to scan |
15:44 | <Josh Goldberg 💖> | I hate to beat the same drum twice in a row, but yes that is also a recommended rule in typescript-eslint 😅 https://typescript-eslint.io/rules/no-duplicate-enum-values Some developers like it for aliasing values. I've personally done this with a /** @deprecated Use (other name) instead. */ sometimes. |
15:45 | <bakkot> | yeah so the goal is to not add a feature which requires that whenever you use it |
15:46 | <shu> | sure, i understand the motivation. my take at this time is how much you'd like to favor public API enum use vs internal enum use |
15:46 | <Andreu Botella> | can we get some help with the notes? |
15:47 | <Michael Ficarra> | do we need to raise a PoO for notes? |
15:49 | <Michael Ficarra> | and attributions! |
15:49 | <Michael Ficarra> | it's super hard to attribute post-hoc |
15:55 | <littledan> | yes, and we make errors there sometimes |
15:58 | <hax (HE Shi-Jun)> | set -> define is very different (much worse) case because it use same syntax but very different semantic. I don't think enum have similar issues. |
16:06 | <Jesse> | can someone help with the notes? I can't take notes rn -- need to go home, make dinner, and come back to the office in < 60 min |
16:07 | <ryzokuken> | Jesse could you stick around for a minute or two? |
16:07 | <ryzokuken> | we should be wrapping up here soon |
16:07 | <bakkot> | I can take over for the next minute Jesse |
16:07 | <bakkot> | go eat |
16:09 | <ryzokuken> | thanks both |
16:46 | <rbuckton> | The TypeScript compiler uses this behavior to define ranges for fast tests against node.kind , like FirstKeyword = BreakKeyword, LastKeyword = OfKeyword , etc., making it very valuable for using enums in performance-critical code. |
16:46 | <rbuckton> | It's also useful when defining a mask that might only contain a single bit. |
16:51 | <ljharb> | the typescript compiler's moving to go, though - are there any other JS use cases? |
16:52 | <snek> | i have done the same thing, in js, not in the typescript compiler |
16:52 | <rbuckton> | The TypeScript compiler isn't the only case, just the most prominent. I use this behavior in other projects as well. |
16:53 | <ljharb> | sorry, to clarify - are these use cases performance-critical in the same way as tsc? |
16:55 | <ryzokuken> | @room back in 5 minutes |
16:55 | <rbuckton> | Some are, yes. There are plenty of other cases, though. For example, I've used them when building a deserializer for a wire format. |
16:58 | <Chris de Almeida> | looking for two TC39 heroes to help with the notes 🙏 |
17:02 | <Aki> | Delegates, if you're presenting at a different part of this meeting, maybe consider taking notes now as a way to pay it forward for when you're speaking |
17:06 | <ljharb> | (i forgot to provide this context: ruben is a node TSC member and core collaborator) |
17:06 | <Chris de Almeida> | @ljharb if you can, please update agenda to include link to slides |
17:06 | <shu> | bless you |
17:08 | <ljharb> | it's a PDF, i'll upload it to the agenda |
17:10 | <jschoi> | TCQ should be switched to the new agenda item so that people can add to the queue, right? |
17:10 | <ryzokuken> | looks updated for me |
17:10 | <ryzokuken> | can you try refreshing?> |
17:11 | <ljharb> | https://github.com/tc39/agendas/blob/main/2025/2025.04%20-%20Object.propertyCount%20slides.pdf |
17:11 | <jschoi> | TCQ should be switched to the new agenda item so that people can add to the queue, right? |
17:12 | <jschoi> | (i forgot to provide this context: ruben is a node TSC member and core collaborator) |
17:12 | <ljharb> | sure, thanks! |
17:16 | <snek> | is this even implementable in a way that makes sense with these options? |
17:16 | <snek> | like unless your object impl is storing these 3 different kinds of keys separately |
17:16 | <snek> | it would be as expensive to get the counts as doing Object.keys etc |
17:18 | <bakkot> | this API is excessively fleshed out for a stage 0 proposal |
17:18 | <Michael Ficarra> | it's probably pretty likely that engines will be differentiating these 3 classes of property keys |
17:18 | <bakkot> | gotta convey to new delegates to avoid that |
17:18 | <Chris de Almeida> | it's going for stage 1 or 2 |
17:18 | <Michael Ficarra> | yeah most people who have been participating in committee have gotten that message, but we definitely don't message it to outsiders |
17:19 | <hax (HE Shi-Jun)> | The alternative should use enumerableKeys and nonEnumerableKeys ? 🤔 |
17:20 | <bakkot> | I would also like to convey to new delegates to avoid trying to go straight for stage 2 when there is any nontrivial design space |
17:20 | <jschoi> | This “Example uses” slide with real-world appearances is important. |
17:20 | <jschoi> | Maybe the most important slide. |
17:20 | <ljharb> | what is the nontrivial part of the design space you see? |
17:20 | <ljharb> | "one method vs multiple methods" seems trivial to me, as does "how are the api options spelled" and "which api options are included" |
17:21 | <Chris de Almeida> | keep in mind Jordan is co-champion on this, so this is not strictly a 'new member' thing |
17:21 | <bakkot> | which variants of kinds of key we allow you to query for, what the shape of the API is |
17:21 | <ljharb> | certainly if you think there's more options than are being presented then it's fine to ensure those are included before stage 2. but the decision which to use likely would be a stage 2 decision imo |
17:22 | <bakkot> | no I mean I think we should do ~none of the options in the proposal |
17:22 | <bakkot> | and that's a large design space |
17:22 | <ljharb> | lol ok well that's def a stage 2 blocking opinion :-) |
17:23 | <bakkot> | also we are not going to get through the queue on this |
17:23 | <bakkot> | not even close |
17:24 | <ptomato> | maybe we should bring Dan Minor's topic forward, so that we can have the rest of the discussion keeping in mind to which degree this pattern is already optimized in practice? |
17:24 | <Michael Ficarra> | yeah this presentation is taking all the available time |
17:25 | <jschoi> | TCQ queue rearrangement would be great for situations just like this. |
17:26 | <jschoi> | Is there a TCQ issue for marking whether your queue item would block the current agenda item or would manifestly affect its conclusion? |
17:26 | <rbuckton> | If we go over, I do have a hard stop at 2:30pm ET and still need to be present for the Explicit Resource Management topic. |
17:28 | <ryzokuken> | there's so much to be requested from TCQ but it's still unclear what the status of the rewrite is |
17:28 | <Chris de Almeida> | hmmm.. might need to move to day 4 if that's a hard constraint |
17:29 | <rbuckton> | I'm not presenting, but I am the champion so I think it's fairly important I'm present. I have to leave at 2:30 to pick up my daughter, so I don't have any wiggle room. |
17:30 | <Justin Ridgewell> | +1 to Shu |
17:31 | <Justin Ridgewell> | Especially the examples with .filter(), I don’t expect that kind of optimization even in higher tier JITs. |
17:31 | <Chris de Almeida> | we could swap with non-extensible topic, but then I think that affects @ljharb's constraint |
17:32 | <Michael Ficarra> | what happened to Dan Minor's item? |
17:33 | <rbuckton> | If we keep it at 2pm and it goes over, I will be back by about 2:50pm if the topic goes the full 30 minutes. |
17:33 | <littledan> | I think the more relevant optimization here is really just about, this will be done in the runtime without allocating an extra array/iterator. Doesn't need actual compiler optimizations. |
17:34 | <shu> | well, allocation sinking / scalar replacement is a common compiler optimization |
17:37 | <snek> | v8 doesn't optimize this does it |
17:38 | <shu> | this particular pattern? nope, not afaik |
17:39 | <snek> | seems ripe for peep hole reduction |
17:40 | <shu> | patches welcome |
17:41 | <littledan> | sure but why would you call propertyCount on something allocated locally? |
17:45 | <shu> | oh i was thinking of doing scalar replacement on the length of locally allocated keys() array |
17:47 | <littledan> | oic, yeah sounds hard |
17:49 | <Bradford Smith> | If you're trying to validate that an Array doesn't have expando properties, couldn't you quite cheaply do Object.countProperties(arr) - arr.length === 0 ? |
17:50 | <bakkot> | sparse arrays :( |
17:50 | <Bradford Smith> | drat |
17:51 | <snek> | just throw if passed a sparse array |
17:51 | <shu> | this is also a good lesson about over-developing the solution |
17:55 | <Michael Ficarra> | I am super uncomfortable with granting stage 1 and then later finding out what we've agreed to when it's added to the notes by the champion |
17:55 | <Michael Ficarra> | I get that we have to move on, but this seems like a failure of our process |
17:55 | <shu> | @erights Mark Miller (Agoric) MM: i am okay with the problem statement "improving performance of counting properties on objects" |
17:56 | <shu> | i need to think more about the problem statement "how to detect sparse arrays and built-in Arrays with non-indexed properties" |
17:56 | <shu> | those are just different problem statements |
17:56 | <shu> | i dislike the pattern of delegates raising questions about what the actual problem statement is, and the champion saying "yeah just add it to the list, that's one of our problems too" |
17:57 | <littledan> | maybe we need a brief overflow topic to agree on what we got to Stage 1 on |
17:58 | <shu> | yeah |
17:58 | <snek> | is the scope not statically whatever block contains the switch case? |
17:58 | <shu> | ehh, kinda |
17:59 | <shu> | but it's a good change that stands alone of implementation opportunities imo |
17:59 | <snek> | oh switch does add a scope, til |
17:59 | <shu> | lexical declarations with on-scope-exit in bare case bodies is hateful |
18:00 | <snek> | oh yeah i'm not arguing in favor of keeping this |
18:00 | <bakkot> | I think Array.isSparse plus Object.countProperties gives you "detecting [non-sparse] arrays with non-indexed properties" as in bradford's example above (basically return !Array.isSparse(arr) && Object.countProperties(arr) === arr.length + 1 ) |
18:00 | <bakkot> | but yeah they're pretty different problem spaces |
18:01 | <ljharb> | it's 100% fine with me personally, if part of the stage 1 exploration results are "make a separate Array.isSparse proposal" (thus removing "index stuff" from this one) |
18:01 | <littledan> | this issue kind of points to how it'd be nice if we moved some more things to "early" time (or a secret third time in the middle?) in the spec's execution model of the world, so we don't gratuitously require this kind of extra logic |
18:05 | <Ruben> | The current options are to differentiate `index`, `nonIndexString`, and `symbol` key types with another option to determine if they should be enumerable or not. Very frequent is the basic case, and I believe no one spoke up against that case: Object.keys(object).length. so enumerable true and index and nonIndexString keys. Another frequent use case is checking if symbols collected with Object.getOwnSymbols are enumerable or not. That is enumerate true or false and symbol key type. One more use case is to validate that an array like Object does not contain any non index string properties. This is a huge performance overhead for these cases. That is either index or nonStringKey type and enumerable true. I believe the latter is the main question about wanting to solve this with the API or not. Another use case is to have a fast path for dense arrays by not having to check if an array is sparse. It would allow to skip additional steps. This would be index type and either enumerability. There are lots of other use cases depending on what a user wants to do. I can collect additional ones if that is requested. |
18:07 | <bakkot> | Of those use cases, I am at this point only convinced that the basic case is worth solving in the language. |
18:09 | <Ruben> | Array.isSparse will address the cheaper one of the two index key types use cases I am aware of. The major performance overhead comes when checking for additional non index string properties on array like objects. That is the one Mark also brought up. For most cases, this will make up most CPU time for validating or comparing these arrays. |
18:09 | <Michael Ficarra> | yes I'm much more comfortable with agreeing to pursue only the first part as part of this proposal, and another case can be made for another proposal |
18:09 | <ljharb> | would folks be receptive to a separate Array.isSparse proposal then? |
18:09 | <ptomato> | we didn't get around to further queue items but I'd like to point out that if there is one frequent use case where it's worth optimizing propertyCount, consider during stage 1 whether it makes more sense to add an API for that specifically |
18:10 | <bakkot> | given adequate motivation |
18:10 | <Michael Ficarra> | I would certainly welcome a presentation that seeks Stage 1 for it |
18:10 | <Ruben> | I can put such a proposal together. Michael Ficarra: would you like to look into this together with me? |
18:11 | <Michael Ficarra> | I would not |
18:11 | <ljharb> | i'm happy to work on it as well, but i'm not sure what bakkot would consider "adequate motivation" |
18:12 | <bakkot> | you've talked about code needing to check for sparse arrays. I personally have never seen code which needed to check for sparse arrays except maybe some extremely pedantic serialization libraries, so giving examples and explaining why it's important for that code would go a long way. |
18:12 | <rbuckton> | static #t = void ... looks like it should just have been static { ... } |
18:14 | <shu> | yeah exactly |
18:14 | <Michael Ficarra> | I would be convinced just seeing that a lot of people are doing it, regardless of whether we think it's "good" |
18:15 | <ljharb> | i mean every user of node's console.log or util.inspect would benefit from the perf increase such a proposal offers |
18:15 | <ljharb> | so that one seems pretty empirical |
18:16 | <bakkot> | I'm especially unconvinced by arguments from performance of node's internals because they always have the option of doing things in C++ |
18:16 | <bakkot> | or zig, or whatever |
18:17 | <snek> | i don't think util.inspect would benefit, it still has to manually identify sparse runs |
18:17 | <Ruben> | From what was discussed: I would remove index vs nonIndexString from the implementation. I believe that was the main pain point. Adding Array.isSparse in combination with the keys would allow the main use cases to be addressed |
18:17 | <Michael Ficarra> | wouldn't we also only care about cases that count properties but avoid enumerating? |
18:17 | <Ruben> | This also simplifies the implementation by not having to worry about what an index key is |
18:18 | <Aki> | Which is the German one that shu hasn't been able to reach? |
18:18 | <ljharb> | with the fast path, only when it finds a sparse thing, so it'd only punish people doing the Bad Thing |
18:19 | <snek> | but it has to iterate over the array anyway |
18:19 | <snek> | to print all the things it contains |
18:19 | <Ruben> | It does that by not allocating the array |
18:19 | <shu> | Aki: it's not the websites themselves, but whoever designed the websites for the two sites listed under "Axial": https://github.com/syg/proposal-nonextensible-applies-to-private/issues/1 |
18:19 | <Ruben> | That would otherwise be a huge overhead |
18:20 | <shu> | it's clear from the HTML source that those two sites were written by the same 3rd party people, so i figured it's not useful to reach out to the sites themselves |
18:20 | <shu> | well, i could ask them who they used directly but i haven't tried |
18:20 | <shu> | not sure if they'd respond to english in any case |
18:22 | <ptomato> | not sure it's in the spirit of the reviewer requirement to speedrun this process? |
18:25 | <Michael Ficarra> | Mark should've written tests, he could've gone for 3 |
18:25 | <littledan> | probably it's not trivial to update all of test262 for this |
18:25 | <bakkot> | I almost wrote tests for getOrInsert just so it could advance |
18:25 | <bakkot> | it's so small... |
18:25 | <Michael Ficarra> | I didn't say it was easy |
18:25 | <bakkot> | and claude is surprisingly good at writing test262 tests |
18:25 | <dminor> | Yeah, I'm not sure what the urgency is here. |
18:26 | <bakkot> | well, there's a little more urgency because this is a breaking change |
18:26 | <bakkot> | albeit of something which is multiple years old at this point |
18:26 | <dminor> | Jonas from the University of Bergen already wrote tests -- https://github.com/tc39/test262/pull/4454 |
18:26 | <snek> | i think there's also just a baseline impatience from knowing that you can only advance stuff at meetings. who wants to wait months for what could be immediate? |
18:26 | <bakkot> | oh nice! |
18:27 | <jschoi> | And with “monotonously (but not asymptomatically)” growing use. |
18:27 | <bakkot> | glad I didn't then |
18:28 | <Chris de Almeida> | tbf, he did say 'please' |
18:29 | <shu> | monotonic not monotonous |
18:31 | <jschoi> | Yes, you are right. I did an eggcorn. |
18:37 | <littledan> | yeah I don't really see this as a source of urgency. But it's more like, if we all agree on it, and it's simple and well-developed, then we can do this multiple advancement thing |
18:40 | <Michael Ficarra> | the chairs have been doing a really great job lately ❤️ |
18:40 | <Chris de Almeida> | The committee may elide the process based on the scope of a change under consideration, as it sees fit. |
18:41 | <littledan> | yep, I agree, that's the clause we're invoking |
18:41 | <Chris de Almeida> | the YOLO clause |
18:41 | <littledan> | well, no, we're not eliding the process |
18:41 | <littledan> | we're following it |
18:41 | <Chris de Almeida> | one of the definitions of elide is 'merge' |
18:42 | <Chris de Almeida> | so I think we are still eliding? 🤷 semantics I suppose |
18:49 | <jschoi> | Is it considered bad form to add multiple queue items at the same time? |
18:49 | <Chris de Almeida> | not at all |
18:49 | <Chris de Almeida> | what's bad form is to add two topics in one item |
18:50 | <jschoi> | Is it still alright to add multiple queue items if they’re added early, before a presentation ends? |
18:50 | <Chris de Almeida> | yes |
19:01 | <Andreu Botella> | there is no transcriptionist at this point |
19:22 | <shu> | Mathieu Hofman: to your question before time ran out, i see a host hook as a layering point for other specs. if the other specs that all embed JS today, HTML being foremost, just end up saying that the host hook does implementation-defined behavior, i think it is misleading editorially to add such a host hook |
19:22 | <shu> | a host hook suggests there is a prescribed or proscribed behavior common to >1 implementation |
19:40 | <Mathieu Hofman> | I see 2 behavior possible for a host hook:
I would very much like some host being able to be clear what their chosen behavior is. While I understand the Web is not interesting in killing the agent cluster, especially if that involves the main renderer thread, I think it makes sense for other hosts. For example maybe a Cloudflare worker would want to terminate the isolate on OOM? Or a node process could opt-in a similar termination (similar to how you can configure termination on unhandled rejection for example) |
20:24 | <shu> | a cloudflare worker is not a host in my mental model |
20:25 | <shu> | we can make it editorially clearer where implementation-defined behavior may occur |
20:25 | <shu> | but the bar to clear a host hook is when there is an embedder spec |
20:25 | <shu> | but even implementation-defined is pretty problematic and most likely unimplementable |
20:26 | <shu> | on mobile OSes, it's not up to the VM to choose. we may be killed by android's OOM-killer even if the engine tries its darndest to resume |
20:27 | <shu> | so to be clear, i'm arguing against the editorial tool of host hook, not against the general thing of getting something in writing about resources |
20:41 | <kriskowal> | There’s a gap for a definition of Host in https://github.com/tc39/how-we-work/blob/main/terminology.md which gives us latitude for interpretation, but by your reckoning, is Node.js or Deno a host? |
20:44 | <shu> | no |
20:44 | <shu> | if there is no spec, i don't consider it a host |
20:45 | <kriskowal> | That is the crux of the disagreement then. I’d qualify host and specification separately. Perl is, after all, a language. |
20:45 | <shu> | i consider node and deno implementations |
20:45 | <shu> | they do not need editorial hooking points in the JS specification if they do not themselves have a specification |
20:50 | <shu> | kriskowal: https://tc39.es/ecma262/#host |
20:57 | <kriskowal> | So, informally, WinterTG might constitute a singular host, because it is a specification that further clarifies the implementation of certain host hooks. There’s no specified notion of an “implementation hook”, but such things exist outside the realm of specifications. |
20:58 | <Andreu Botella> | We (WinterTC) would have to agree on whether there's something to specify in that implementation before providing a spec though |
20:58 | <Andreu Botella> | but yeah, we could do that |
20:59 | <kriskowal> | This framing does help qualify worthiness of consideration, and from that emerges the hooks available to implementations that exist outside specified hooks, regardless of worthiness of consideration. |
21:00 | <shu> | yeah, if WinterTG produces a spec for all JS runtimes to conform to, then that would be a singular host |
21:00 | <kriskowal> | That is, puts Node.js on strange footing here, if they for example sought to be able to get a heap snapshot at the exact moment of a reference error. |
21:01 | <kriskowal> | All JS runtimes seems like a reach. Is not the purpose of a host to differentiate a subset of implementations for more specific considerations? |
21:02 | <kriskowal> | Take TG-53 as a more concrete example. They specify all manner of things that browsers are not obliged to implement, at the embedded system bindings. |
21:13 | <shu> | all JS runtimes that seek to conform to whatever WinterTG produces |
22:03 | <littledan> | *WinterTC |
22:05 | <shu> | ah sorry yeah |
22:06 | <littledan> | a lot of the host hooks for WinterTC environments will be provided by the HTML spec, but yeah there might be some which we define differently at the minimum-common-api level |
22:08 | <littledan> | and there might be some which are defined per-server-environment, in principle |
22:08 | <littledan> | we haven't worked out everything about how modules work yet; that's a case that will somehow be mixed |
22:13 | <shu> | kriskowal: to be clear i'm not against calling out editorially that there may be implementation-defined behavior for OOMs |
22:14 | <shu> | the bigger issue is i don't think it's feasibly implementable for an implementer of JS or embedder of a JS engine to choose "always try to resume" or "always crash agent cluster" |
22:18 | <kriskowal> | On that, we agree. |
22:19 | <kriskowal> | I’m encouraging Mark to make this clear: we do not seek to impose either of those semantics on the web. We’re seeking a place to stand for non-web “implementations”. |
22:20 | <shu> | i think if the non-web implementations have a specification, then a host hook is fair game to be discussed. if it's really just your implementation (XS), then i think it's fine to call out the possibility of this behavior editorially, without a host hook |
22:20 | <kriskowal> | And also not just for our own transactional wonderland. There are intermediate points in the spectrum from “availability before all else” and “transactions”. |
22:22 | <shu> | metapoint here is that i am bemused by mark's worldview to reason at this level of fault tolerance |
22:23 | <kriskowal> | A specific case I would advocate for as a user of Node.js in any unattended distributed system is to leave a pretty corpse for the autopsy. |
22:23 | <shu> | i suppose i'm used to a world where UB is a convenient catchall for real world issues like this |
22:24 | <shu> | but since we strictly speaking do not have UB, the logical conclusion of that interpretation is all real world resource faults must be specified as well. but that seems intractable to me so i think i just never gave it much thought |
22:24 | <shu> | like, surely solar flares causing random bit flips in your RAM cannot be meaningfully guarded against by a language specification? |
22:25 | <kriskowal> | I mean, it is pretty spectacular. I’m bemused that I have the privilege of considering resilience against stack overflow, for an attacker that prepares for a stack n frames below the top in order to force specific invariants to be violated. |
22:25 | <shu> | but what's your line-drawing exercise, is what i'm asking |
22:26 | <kriskowal> | at risk of the appearance of being anything less than an expert in this field (hi), what do the initials UB expand to? |
22:27 | <shu> | undefined behavior |
22:27 | <kriskowal> | oh, well then. |
22:27 | <shu> | actually i have no idea if C++ considers real world resource faults to be UB |
22:27 | <kriskowal> | real world resource faults are all sorts of hell |
22:28 | <kriskowal> | nevermind OOM, linux sbrk literally never returns an error. it prefers to give you a page you will fault on first touch |
22:28 | <shu> | well, concrete question for you: what's the principled reason that distinguishes solar flares causing bit flips and OOMs, or are two events in the same category? |
22:29 | <shu> | or not even solar flares, just freak bit flips. that happens |
22:30 | <kriskowal> | ah, but adversarial solar flares, that’s the real trick |
22:30 | <kriskowal> | adversarial solar flares are of course cosmic rays deliberately intended to exploit knowledge about your toolchain in order to force intentional outcomes |
22:31 | <shu> | what i'm driving at is, if the pedantic interpretation is that implementations of JS today that can OOM are non-conformant because the spec assumes infinite, non-faulty memory |
22:31 | <kriskowal> | i believe the differentiating line is probability, and whether the threat mechanism can be exploited by an adversary |
22:31 | <shu> | conformance is not probabalistic |
22:31 | <shu> | i'm not against having such a notion, to be clear, i think likelihood of conformance is a much more useful measure |
22:32 | <shu> | but that's not what marking was arguing for |
22:32 | <kriskowal> | ah, i cannot speak to the broader point about the impossibility of satisfying both of the desirable traits of a system |
22:33 | <shu> | we should discuss this over drinks, i might even enjoy myself. but i don't think it's a great use of committee time to get an editorial carveout |
22:33 | <kriskowal> | i am certainly more aligned with the position that we cannot have perfect outcomes in the absolute |
23:33 | <jschoi> | Would nuclear-weapon EMPs fall under “adversarial solar flares”? Or, really, any artificial EMP… |
23:39 | <kriskowal> | I mean, if your threat model is deterministic termination, you terminate deterministically. |