00:46 | <ljharb> | sure, i can unarchive it if you want to comment it yourself? just lmk, or if we can't coordinate, i can |
00:47 | <ljharb> | also, what happened with the "set method argument internal slot" discussion? i had to drop off |
03:32 | <bakkot> | ljharb: if you want to unarchive tonight and ping me I'll link it, or you should feel free to do so on my behalf |
03:33 | <bakkot> | also, what happened with the "set method argument internal slot" discussion? i had to drop off Set.prototype.union , MM does not like the idea of only reaching in to the [[SetData]] internal slot, but would potentially be OK with reaching in to that slot if present and otherwise falling back to the publicly exposed methods (has , etc), even though this would technically be a violation of proxy transparency (because for most cases it would still Just Work) |
03:33 | <ljharb> | bakkot: done now, go nuts |
03:34 | <ljharb> | bakkot: but the receiver would still have slot access only, yes? |
03:34 | <bakkot> | yeah, receiver is fine |
03:34 | <bakkot> | that's already how it works |
03:34 | <ljharb> | awesome |
03:34 | <bakkot> | that is, it is already common to access the internal slot of the receiver |
03:34 | <ljharb> | (re-archived the repo, after your comment; lmk if you need anything else) |
03:35 | <bakkot> | nope, that was all, thanks |
03:35 | <bakkot> | though in both the argument case and the receiver case we need to figure out what affordances, if any, we're going to make for subclasses |
16:38 | <Jack Works> | hi I've read the meeting logs yesterday. I'm interested in reviewing structured clone algr. (cc syg ) |
16:40 | <shu> | Jack Works: sure, will request your review when the draft is ready |
16:49 | <Jack Works> | 👀 I set an alarm on 14:00 PST and not be able to present enum before 14:00 cause I'm sleeping. |
16:52 | <Rob Palmer> | Jack Works: we'll schedule you as late as possible today to help you sleep - so in between 14:00-15:00 PST |
17:57 | <Rob Palmer> | This is your 4 minute warning: Plenary starts soon |
18:02 | <ptomato> | I can't do notes right away, but I can in a bit |
18:03 | <Rob Palmer> | thanks ptomato |
18:27 | <bakkot> | Mathieu Hofman: can you confirm the conclusion we have captured in the notes is correct |
18:27 | <bakkot> | I think so but best to confirm |
18:44 | <bakkot> | never GCing is always legal, so XS's implementation would still be conformant |
19:00 | <ljharb> | +1, that was a mistake |
19:00 | <ljharb> | (but i also think registered symbols themselves are a mistake) |
19:04 | <shu> | what was the original use case for them anyway |
19:05 | <bakkot> | for registered symbols? |
19:05 | <bakkot> | it lets you do the same thing as well-known symbols for libraries |
19:05 | <bakkot> | so a library can interop with itself across realms |
19:05 | <shu> | but... what realms |
19:05 | <shu> | we didn't have sync realms |
19:05 | <bakkot> | iframes |
19:05 | <bakkot> | we have always had iframes |
19:05 | <shu> | oh, i guess sync iframes |
19:05 | <bakkot> | but also, not necessarily across realms |
19:05 | <shu> | i have a hard time believing that was a use case tc39 cared about back then? |
19:05 | <bakkot> | just multiple version of the library within the same realm |
19:06 | <yulia> | Ashley Claymore: can you share with me the test case you used to identify SM gc behavior? |
19:06 | <shu> | yes, right, coordination without having the library do the heavy lifting |
19:07 | <Ashley Claymore> | Ashley Claymore: can you share with me the test case you used to identify SM gc behavior? |
19:07 | <bakkot> | well, also lets consumers of a library coordinate with it without having a direct reference to it |
19:07 | <bakkot> | like you can have a library which defines a protocol, and have someone else implement that protocol without reference to the library |
19:09 | <ljharb> | shu: as much as i claim that the existence of iframes means browsers can't pretend realms don't exist, i completely agree with you that it's unlikely that was the motivation |
19:09 | <ljharb> | making the symbol registry realm-specific would have supported the library use case just fine, i think |
19:09 | <bakkot> | forget I said realms; "multiple versions of a library" is the right thing to think about, whether that's cross-realm or not |
19:09 | <bakkot> | and consumers of a library which aren't including it directly |
19:10 | <ljharb> | erights: what if i had a WeakMap of a symbol to an object, and then a WeakRef of the same object, and no other refs to the object. couldn't i observe the collection of the symbol via the collection of the object? |
19:19 | <Richard Gibson> | subclassing doesn't work anyway, because of e.g. BaseClass[method].call(subclassInstance, …) directly interacting with internal slots |
19:20 | <bakkot> | ljharb: fwiw I don't think "registered symbols" and "unique symbols" are actually the same kind of thing for users |
19:20 | <bakkot> | they have the same type but they really do not come up in the same cases |
19:21 | <Ben Newman (Apollo, @benjamn on GH)> | Aren't registered Symbols often used for safely branding objects, in case you have more the one copy of a library (so instanceof is risky)? |
19:21 | <bakkot> | Ben Newman (Apollo, @benjamn on GH): yes, but that's not like a regular symbol |
19:22 | <Ben Newman (Apollo, @benjamn on GH)> | I was responding to the question about whether anyone actually uses registered Symbols |
19:22 | <bakkot> | ah, sure |
19:22 | <ljharb> | that's the common appropriate use case, yeah |
19:24 | <Ben Newman (Apollo, @benjamn on GH)> | "eternal" seems to mean/imply "recoverable after all references are lost" |
19:24 | <bakkot> | Ben Newman (Apollo, @benjamn on GH): my claim is that there is no good reason to want to put such a brand in a WeakMap |
19:24 | <bakkot> | at least not that i can think of offhand |
19:25 | <Ben Newman (Apollo, @benjamn on GH)> | I'm uncomfortable with throwing "good" around like that |
19:25 | <ljharb> | hm, i just got kicked off the call |
19:25 | <nicolo-ribaudo> | Me too |
19:25 | <ljharb> | what did i miss? |
19:26 | <ljharb> | ah k |
19:26 | <rickbutton> | did the call just die? |
19:26 | <ptomato> | same |
19:26 | <Ben Newman (Apollo, @benjamn on GH)> | (it's back if you reconnect) |
19:27 | <nicolo-ribaudo> | * Oh again |
19:30 | <bakkot> | let me put it a different way: the reason to want a registered symbol is that you want something which lives forever, for e.g. coordination across instances/consumers of a library. the reason you want to put symbols in a weakmap is that you want to hold something weakly - you have a symbol which is ephemeral, and you want to hold something else ephemerally. (Otherwise you could just use a Map.) these uses are directly opposed. |
19:31 | <Ben Newman (Apollo, @benjamn on GH)> | I'm fine with never collecting registered symbols, as are V8 and Mozilla's engine, it sounds like |
19:31 | <Ben Newman (Apollo, @benjamn on GH)> | they are, quite literally, always reachable once registered, even if you've lost all references |
19:32 | <rbuckton> | bakkot: But you can also intentionally craft an object that should live forever and also place it in a weakmap. If you want to track a symbol in a way that is "preferrably" weak, you would have to write a lot of defensive code and have both a WeakMap and a Map. |
19:33 | <Ben Newman (Apollo, @benjamn on GH)> | the "optimization" of collecting registered symbols anyway and then later returning a new reference if someone asks for them again with Symbol.for is simply unsound, IMO |
19:33 | <Ben Newman (Apollo, @benjamn on GH)> | it would be nice to hear about a JS engine that actually does that |
19:34 | <Ben Newman (Apollo, @benjamn on GH)> | is someone from Apple on the call? msaboff ? does Safari actually collect registered symbols? |
19:34 | <bakkot> | rbuckton: my position is that the desire to weakly hold symbols which may or may not be registered is niche enough that it's sufficient for the language to support it without needing to make it trivial |
19:34 | <yulia> | Robin Ricard: can you add "no preference, general support"? |
19:35 | <Ben Newman (Apollo, @benjamn on GH)> | XS does not collect any symbols (just stated by Peter H on the call) |
19:35 | <yulia> | i mean, i care, i like it |
19:35 | <rickbutton> | yulia: "indifferent" |
19:35 | <shu> | Ben Newman (Apollo, @benjamn on GH): why is that unsound? |
19:35 | <Robin Ricard> | changed wording |
19:35 | <shu> | it happens with strings all the time |
19:35 | <shu> | and doubles, actually, in V8 |
19:36 | <Ben Newman (Apollo, @benjamn on GH)> | @shu because the reference shouldn't change in any semantically observable way, and the WeakMap question provides observability |
19:36 | <Ben Newman (Apollo, @benjamn on GH)> | strings aren't stored in [weak]maps/sets by reference though |
19:36 | <Ben Newman (Apollo, @benjamn on GH)> | they're stored by value |
19:36 | <bakkot> | it's only unsound if WeakMaps can hold registered symbols |
19:36 | <shu> | strings can't be put into weak collections at all |
19:36 | <bakkot> | (and doing so does not prevent GC of the symbol) |
19:36 | <shu> | correct |
19:37 | <Ben Newman (Apollo, @benjamn on GH)> | I'm saying it's unsound anyway, and this is just the first gotcha we've encountered |
19:37 | <shu> | it's... not unsound anyway |
19:37 | <Ben Newman (Apollo, @benjamn on GH)> | it's sound by accident, currently |
19:37 | <bakkot> | wasn't an accident |
19:37 | <bakkot> | that was definitely an intentional part of the design of the symbol registry |
19:38 | <shu> | yes, they were accidentally eternal before, when it was keyed by something else, like parse nodes or something? |
19:38 | <yulia> | i have the screenshots if anyone needs them |
19:38 | <bakkot> | you're thinking of template tags |
19:38 | <yulia> | cc Robin Ricard |
19:38 | <shu> | oh, i am, bakkot correct |
19:41 | <Robin Ricard> | thanks yulia, nicolo took them as well so we are good |
19:43 | <Ben Newman (Apollo, @benjamn on GH)> | catch the positive names? |
19:50 | <TabAtkins> | shu: "We should do the same as others" isn't quite the point, "everyone else is doing something useful that we aren't" is. |
19:52 | <shu> | TabAtkins: can i cast your own spell on you? isn't that a fully general argument for stdlib differences? |
19:53 | <TabAtkins> | No, it's just pointing out that the argument wasn't just "we should be following the bandwagon". |
19:53 | <shu> | ah |
19:53 | <shu> | i was responding to the narrower motivating story of "even surma got tripped up" |
19:54 | <TabAtkins> | The other lang's version of split() is just genuinely better, which is definitely part of why JS's version is so confusing. ^_^ |
19:54 | <shu> | which isn't the same as "i wish we had this other behavior but we don't", it's "i thought our behavior was X but it wasn't" |
19:54 | <bakkot> | I will try to remember to ask my dad about the history of this next time I talk to him |
19:55 | <TabAtkins> | Insofar as Surma would have been confused by Python (off-by-one versus the other langs), sure. But it's not clear from his particular complaint whether it was just familiarity or "i'm confused this works differently entirely" |
19:55 | <leobalter> | I'd like if we rename this splitn to String.prototype.part (or parts) |
20:00 | <TabAtkins> | Going for the {remainder: true} part and taking on Python's numbering semantics would defuse these complains, I think. |
20:00 | <Michael Ficarra> | I have an open issue about the possible confusion that Chip is talking about: https://github.com/lucacasonato/proposal-reversible-string-split/issues/6 |
20:01 | <TabAtkins> | So .split(..., 2, ...) always returns 2 bits between separators, you just get a third item with the remainder if you pass the flag. |
20:02 | <bakkot> |
|
20:02 | <bakkot> | say more about what that means? |
20:05 | <ljharb> | Luca Casonato: i invited you to tc39-transfer on github, so you can bounce your proposal repo there |
20:06 | <TabAtkins> | bakkot: Well there was an "and" there that was pretty important. |
20:06 | <TabAtkins> | The part after the "and" that you quoted was more of an "(and, in effect, ...)" |
20:07 | <Luca Casonato> | I'd like if we rename this splitn to |
20:08 | <TabAtkins> | But basically, if we use a flag argument in .split(), I don't think we should significantly change the existing semantics/behavior of the proposal. .split(..., 2) should still always trigger (up to) 2 splits, with the option just controlling whether we get the remainder or not included in the array. |
20:08 | <TabAtkins> | That also avoids all the regex questions, since the answer remains "act exactly as normal, just include the remainder as a final item" |
20:11 | <Surma> | (sounds like Luca Casonato got stage 1? :D) |
20:11 | <TabAtkins> | yes |
20:12 | <Luca Casonato> | (sounds like Luca Casonato got stage 1? :D) |
20:12 | <leobalter> | Could you add that to this issue? https://github.com/lucacasonato/proposal-reversible-string-split/issues/6 |
20:13 | <Luca Casonato> | But basically, if we use a flag argument in .split(), I don't think we should significantly change the existing semantics/behavior of the proposal. |
20:13 | <TabAtkins> | If people already don't know about the second argument, how will they find a completely separate method? |
20:13 | <Luca Casonato> | If people already don't know about the second argument, how will they find a completely separate method? |
20:14 | <TabAtkins> | Editors often offer signature suggestions too ^_^ |
20:16 | <Luca Casonato> | That also avoids all the regex questions, since the answer remains "act exactly as normal, just include the remainder as a final item" |
20:17 | <Luca Casonato> | Really, I dislike this capturing group expansion into the return value array behaviour. It significantly increases complexity |
20:19 | <TabAtkins> | Ugggggh I didn't realize the N is literally "length of the returned array" even when regex capture groups are used, that's worthless. |
20:19 | <ljharb> | does anyone know who https://github.com/phohensee is, in relation to tc39? |
20:22 | <Luca Casonato> | Ugggggh I didn't realize the N is literally "length of the returned array" even when regex capture groups are used, that's worthless. |
20:23 | <HE Shi-Jun> | Yeah the currrent split behavior is just useless |
20:23 | <Luca Casonato> | "".split(sep, n) === "".split(sep).slice(0, n) |
20:24 | <ljharb> | for n >= 0, i presume |
20:25 | <HE Shi-Jun> | I think we'd better follow rust, splitN(n, s) seems very clear and won't confused with the old one. |
20:25 | <Luca Casonato> | for n >= 0, i presume
|
20:30 | <Michael Ficarra> | 🎉 we just got consensus at the UTC meeting for my proposal to stabilise the spelling of property names/values/aliases! https://www.unicode.org/L2/L2022/22029-canonical-prop-spelling.pdf |
20:31 | <Michael Ficarra> | we'll be able to delete tables 67 and 68 now |
20:41 | <Ben Newman (Apollo, @benjamn on GH)> | * table 69 is breathing a sigh of relief right now |
20:59 | <Rob Palmer> | the meeting resumes in 60 seconds |
21:03 | <bakkot> | Michael Ficarra: did you ask them why they recommended loose matching in the first place? |
21:03 | <Michael Ficarra> | no, though it sounded like it's just kind of their default position |
21:03 | <bakkot> | weird |
21:04 | <Michael Ficarra> | someone was like "can't we just make it stable but not add it to our stability policy?" and I was like "uuuhhhh that's not gonna work" lol |
21:05 | <Michael Ficarra> | imo the only reason they would want that is if they planned to break it |
21:07 | <Michael Ficarra> | why do we allow this form in any position other than the target of a call? do we really want it being passed around? |
21:08 | <ljharb> | which form? |
21:08 | <Michael Ficarra> | class.hasInstance |
21:08 | <ljharb> | i assume class.hasInstance() would work just like super() and import() , in that it's not a real function and can't be passed around |
21:08 | <Michael Ficarra> | exactly, that's what I'm suggesting |
21:09 | <ljharb> | oh, is this proposal currently suggesting something different? |
21:09 | <Michael Ficarra> | that's how I understood the presenter, maybe not? |
21:09 | <Michael Ficarra> | I didn't look at spec text for this proposal |
21:10 | <ljharb> | for stage 2 i'd definitely insist alongside you that that's how it work ¯\_(ツ)_/¯ |
21:10 | <Michael Ficarra> | oh no your arm |
21:10 | <ljharb> | lol matrix is even worse than github with the markdown escaping (-‸ლ) |
21:12 | <shu> | i thought it was a meta property call syntax, yeah |
21:12 | <shu> | how do you even pass it around |
21:13 | <ljharb> | if it were a real function you could foo(class.hasInstance) but obv that'd be subpar |
21:13 | <shu> | well right, but i didn't think it was a real function |
21:15 | <Michael Ficarra> | someone want to ask the clarifying question? I'm kinda in the middle of my lunch |
21:15 | <bakkot> | i am not so convinced by the "you might end up with a partially constructed instance" problem. you have to work pretty hard to still end up with a reference to the instance when a field initializer throws; it's not something you're going to end up passing around natuarlly |
21:16 | <shu> | i am not understanding what is being proposed for the synthetic brand he wants class.hasInstance to make |
21:16 | <shu> | at the start or at the end? |
21:16 | <shu> | oh here's the slide |
21:17 | <bakkot> | "after the constructor returns" does seem like the right answer to this question to me yeah |
21:18 | <Michael Ficarra> | oh THAT'S what he meant by function-like |
21:19 | <bakkot> | does import() work inside eval? |
21:19 | <bakkot> | i am guessing yes, so I would say this should also work |
21:19 | <bakkot> | "if there is a direct eval you have to do a lot more work" is not a new thing |
21:20 | <shu> | super does |
21:20 | <shu> | and super already causes extra allocation for home objects |
21:20 | <shu> | so eval also causes that |
21:20 | <shu> | it's no worse than the status quo, which remains "don't use eval" |
21:21 | <ljharb> | private fields don't tho, i think |
21:21 | <shu> | really? |
21:21 | <ljharb> | ~hm, let me confirm~ no nvm, they do work |
21:21 | <rbuckton> | I need to draft an update on class access expressions. Assuming it will ever move forward, I think I need to move it to a meta property like class.static or something. |
21:22 | <rbuckton> | or class.constructor maybe. |
21:22 | <ptomato> | I think we're managing just adequately with the notes, so no need to interrupt the presentation to ask for more, but if someone wants to help out I'd nonetheless appreciate it |
21:23 | <Michael Ficarra> | rbuckton: for anonymous classes or something? |
21:25 | <rbuckton> | Its original intent was to handle multiple things: anonymous classes and giving a consistent name to access statics to avoid the this.#foo footgun in static methods |
21:26 | <ljharb> | it's also nice to avoid repeating the class name - gives you only one thing to change if you want to rename a class declaration |
21:26 | <Rob Palmer> | so hasInstance() true implies the object was fully constructed without throwing |
21:27 | <shu> | all non-throwing cases can be, yes |
21:27 | <Michael Ficarra> | then why did we even expose an immutable binding inside the class? I'm unconvinced |
21:27 | <shu> |
|
21:28 | <shu> | what the hell |
21:28 | <shu> | i typed ~~~~recursion~~~~~~ |
21:28 | <ljharb> | yulia: i do agree that class.hasInstance is always equivalent to a private field at the end of the class body, that's assigned to sentinel at the end of the constructor, and doing #field in o && o.#field === sentinel |
21:28 | <shu> | it's not equivalent to private field at the end, is it? |
21:28 | <Rob Palmer> | to replicate this feature with ergonomic brand checks, i think the user would have to assign to the brand field at each return point in the constructor |
21:28 | <shu> | yeah, it's like that, but that's also why i'm kinda not super convinced |
21:28 | <ljharb> | Michael Ficarra good question. i don't find that binding immutability particularly useful, and would have preferred a class access expression |
21:28 | <shu> | but i'll ask during my queue item |
21:29 | <rbuckton> | then why did we even expose an immutable binding inside the class? I'm unconvinced |
21:29 | <Michael Ficarra> | ljharb: I think it speaks to the intentions of the delegates who designed the class proposal |
21:29 | <bakkot> | to replicate this feature with ergonomic brand checks, i think the user would have to assign to the brand field at each return point in the constructor this during the constructor even if the constructor throws, right? if you assume the ctor/initializers don't throw it's equivalent? |
21:29 | <Ashley Claymore> | https://github.com/tc39/proposal-class-brand-check/issues/6#issuecomment-1015303592 |
21:30 | <shu> | Michael Ficarra: it might be just copying the function scope |
21:30 | <shu> | named function expressions also have that constant lambda scope |
21:30 | <ljharb> | Michael Ficarra: since some of them are here, it'd be nice to hear their intentions spoken instead of inferring them |
21:30 | <ljharb> | but yes, it's the same way functions work |
21:30 | <bakkot> | if so, as I said above, i am not so convinced by the "you might end up with a partially constructed instance" problem |
21:30 | <Michael Ficarra> | shu: they have their own name scope, but it's mutable |
21:30 | <ljharb> | which fwiw is also subpar, it's just that self-recursion is much rarer than accessing the constructor's statics |
21:30 | <shu> | what |
21:30 | <shu> | the lambda scope is not mutable |
21:30 | <Michael Ficarra> | uhh I thought it was, let me check |
21:31 | <bakkot> | nope |
21:31 | <bakkot> | you can assign to it but it's immutable |
21:31 | <shu> | yeah |
21:31 | <bakkot> | it's a unique kind of const among bindings in JS |
21:31 | <shu> | it's not the same semantics as const but it's immutable |
21:31 | <shu> | so good |
21:31 | <Michael Ficarra> | ugh weird |
21:31 | <bakkot> | which fwiw is also subpar, it's just that self-recursion is much rarer than accessing the constructor's statics |
21:32 | <ljharb> | doubt which, the rarity? |
21:32 | <Rob Palmer> | only because someone might get a hold of this to a static function that contains hasInstance from the same class |
21:32 | <shu> | ugh weird |
21:32 | <bakkot> | yes |
21:32 | <ljharb> | bakkot: recursion at all is rare in JS due to a lack of PTC |
21:32 | <ljharb> | it's certainly all over the place, but way rarer than it would be otherwise |
21:32 | <shu> | ...false? |
21:32 | <bakkot> | this has not been my experience |
21:32 | <ljharb> | perhaps we look at very different kinds of codebases |
21:32 | <shu> | most useful recursion i argue is in fact not tail recursion |
21:33 | <Michael Ficarra> | everything is tail recursion |
21:33 | <waldemar> | How does Contains work with respect to a class? If you're evaluating Contains on Expr which contains a class expression C, does Contains peek into C's computed property names? |
21:33 | <Justin Ridgewell> | I do recursion all the time. |
21:33 | <bakkot> | waldemar: there is a special ComputedPropertyContains which, yes, descends into the computed property names, but not into method bodies or initializers |
21:33 | <waldemar> | And if you're doing Contains on C itself, does it peek into its own property names? |
21:34 | <bakkot> | That one I'm not sure of offhand |
21:34 | <bakkot> | looks like, yes, it peeks into its names but not into method bodies, same as if you called Contains on a parent of C |
21:34 | <waldemar> | It seems that both do, which is weird. Things in computed property names are both in the class and not in the class? |
21:35 | <waldemar> | This is a problem for things which are class-scoped like the proposal just presented. |
21:35 | <bakkot> | We'd need to use a different operation than Contains, yes |
21:36 | <bakkot> | Contains normally stops at function boundaries - which you can read to mean "places where yield might start meaning a different thing", to be precise - which is why it looks into computed property names but not method bodies |
21:37 | <waldemar> | Hax's proposal makes it pierce function boundaries |
21:37 | <waldemar> | Methinks Contains is getting too overloaded and confusing |
21:37 | <bakkot> | I would suggest introducing a different operation, yes |
21:38 | <bakkot> | we've done that before - e.g. we have ContainsArguments for looking for arguments in field initializers, which is like Contains but with some details different (it descends into arrows, in particular) |
21:38 | <bakkot> | adding a similar new ContainsClassHasInstance is probably the way to go |
21:48 | <HE Shi-Jun> | Currently we use ContainsClassHasInstance to check whether a class scope include any class.hasInstance. Maybe we don't need modify Contains... Speccing such things is too hard for me or TianYang 😂 |
21:49 | <HE Shi-Jun> | Actually we already rewrite the spec text at least three times. |
21:52 | <HE Shi-Jun> | It seems that both do, which is weird. Things in computed property names are both in the class and not in the class? class.hasInstance in computed property syntax error. Though as current spec text, (if I really figure out all things) i believe it's allowed but always give u false, because at that time, no one have the class so no instance of it. |
21:53 | <HE Shi-Jun> | Hax's proposal makes it pierce function boundaries |
21:54 | <bakkot> | If you figure out exactly what the semantics you want I'm happy to help with writing the spec text before stage 3 |
21:54 | <bakkot> | at least in terms of telling what is probably the simplest way to write the thing you want |
21:54 | <bakkot> | important part is figuring out the semantics you want |
21:59 | <Rick Waldron> | rbuckton: I just realized that I'm going to miss the Enum proposal because I have to go get my kid from school. |
22:00 | <bakkot> | waldemar: if it's a syntax error [in computed names] then it doesn't refer to either the inner or outer class, because it's unutterable |
22:00 | <bakkot> | which seems like a good approach to me |
22:00 | <rbuckton> | rbuckton: I just realized that I'm going to miss the Enum proposal because I have to go get my kid from school. |
22:01 | <HE Shi-Jun> | sorry i lose the connection |
22:05 | <Justin Ridgewell> | ~Do we have a link to the slides?~ nevermind, the agenda was updated |
22:10 | <Mathieu Hofman> | Mathieu Hofman: can you confirm the conclusion we have captured in the notes is correct |
22:11 | <Ashley Claymore> | I wonder if we could have a more general construct for 'run this on successful return' . catch for errors, finally for all exits and something else for completed without error |
22:11 | <Ashley Claymore> | </half-baked-idea> |
22:11 | <bakkot> | Looks right to me. And PR has been updated. |
22:12 | <bakkot> | Ashley Claymore: there's been proposals for like a catch.error meta-property you could use in finally which would tell you what the error is in that case |
22:12 | <ljharb> | Ashley Claymore: isn't that just "add a statement at the end of the try " tho |
22:12 | <bakkot> | which lets you just check in your finally if you are in the completed without error case |
22:12 | <bakkot> | ljharb: end of the try isn't necessarily reached if there's a return |
22:13 | <ljharb> | (to add that now we'd have to solve it both for syntactic and Promise finally ) |
22:13 | <ljharb> | oh true |
22:13 | <bakkot> | strong disagree with the claim that any functionality available in syntactic finally must also be available in Promise.finally |
22:13 | <bakkot> | if that was the claim |
22:13 | <Ashley Claymore> | Ashley Claymore: isn't that just "add a statement at the end of the |
22:13 | <ljharb> | it was, because in the past that was one of the stated blockers for three-state promises - iow, that claim has precedent. |
22:14 | <ljharb> | eg the whole catchCancel thing |
22:14 | <bakkot> | ehhhhhhhhhhhhhhhhhhhhh |
22:14 | <bakkot> | don't really agree with that on multiple levels |
22:14 | <ljharb> | none the less, that parity was a huge part of the design of Promise, and it constrained me on Promise finally as well |
22:15 | <ljharb> | any motivating use case for catch.error would exist in promise finally, so i'm not sure i understand why it'd be ok to do just one |
22:15 | <bakkot> | you can achieve the same thing with .then and .catch if you really want to |
22:16 | <ljharb> | you can achieve it with a boolean flag in .catch too. |
22:16 | <bakkot> | yeah but it's gross |
22:16 | <bakkot> |
you can add a meta-property to solve it in syntax, and you cannot do that to add it in |
22:16 | <ljharb> | right, it'd have to be an argument to the finally callback somehow |
22:16 | <bakkot> | "there is a good way to do this in one case, and not in this other" is a fine reason to provide an affordance in one case and not the other |
22:16 | <Mathieu Hofman> | Ashley Claymore: there's been proposals for like a I have on my list of wishes to add an optional error argument to the
|
22:17 | <Ashley Claymore> | what if someone throws falsey |
22:17 | <ljharb> | Mathieu Hofman: you can throw and reject with undefined , so it'd have to be a container |
22:17 | <yulia> | you can implement this with private fields though, so it is a usecase that is possible with it -- is it being used for that? |
22:17 | <waldemar> |
It's not always false. You can define a function inside a computed property name expression, squirrel it away somewhere, and call it later. |
22:17 | <Mathieu Hofman> | and allow spread so you can do finally (...errs) { if (errs.length); } to handle the err === undefined case |
22:18 | <ljharb> | i suppose that'd work in .finally with arguments.length |
22:18 | <Mathieu Hofman> | And similar for Promise.p.finally |
22:18 | <Ashley Claymore> | Also there's not a way to only freeze prototype right, though I think there is a proposal for that right? |
22:18 | <bakkot> | anyway maybe it would prove possible to add an argument to the Promise.finally callback, which would be fine; but if not I don't think that needs to block the syntax |
22:19 | <bakkot> | Also there's not a way to only freeze prototype right, though I think there is a proposal for that right? |
22:19 | <bakkot> | need to revisit and figure out how to work with the rest of the traps, like [[IsExtensible]] |
22:20 | <ljharb> | i'd love to see that freeze proto one advance |
22:20 | <shu> | override mistake be damned? |
22:20 | <ljharb> | ? how does it conflict with that? |
22:21 | <shu> | oh, maybe i misunderstood what the freeze proto proposal does |
22:21 | <ljharb> | iiuc it'd make Object.prototype not be exotic, because it'd make "a fully mutable object with a frozen [[Prototype]]" a normally achievable thing |
22:22 | <shu> | yeah i completely misunderstood, please ignore |
22:22 | <bakkot> | yeah, just freezes the prototype slot itself, not the prototype object |
22:22 | <bakkot> | override mistake be damned? |
22:22 | <ljharb> | lol |
22:22 | <shu> | if you can get someone else to own the breakage fallout bugs on v8, sure |
22:23 | <bakkot> | really it's more "be damned" in the sense of "damn it" |
22:26 | <yulia> | HE Shi-Jun: posted https://github.com/tc39/proposal-class-brand-check/issues/15 |
22:27 | <bakkot> | ljharb: ok, so, syntactic finally is already unlike Promise.prototype.finally , in that syntactic finally can override the return value, and Promise.prototype.finally cannot |
22:27 | <bakkot> | so how does that not already break the equivalence? |
22:28 | <HE Shi-Jun> |
|
22:30 | <bakkot> | I like syntax error, syntax error is good - there is no reason to want to write this code |
22:31 | <ljharb> | bakkot: you're right, that is the one way it's unlike; and given that it's impossible via the callback, we all decided to accept that difference. but that doesn't mean further avoidable deviation is a good idea |
22:32 | <bakkot> | sure. depends on how avoidable it is. |
22:32 | <Michael Ficarra> | I am so incredibly opposed to auto-increment, I cannot find the words to describe it |
22:34 | <ljharb> | so for the enum proposal - this is a ton of discussion about semantics, which are more of a stage 1+ concern. is it worth a 💩 to talk about problem space/motivation? |
22:34 | <ljharb> | sorry that's a "point of order" initialism; my autocorrect took over |
22:34 | <Michael Ficarra> | ljharb: agreed, was thinking the same thing |
22:34 | <Michael Ficarra> | this is cool and all, but the more important bit is to convince us that there's a problem worth solving here |
22:34 | <Justin Ridgewell> | Lessons from protobuf are that anything other than explicitly id'd values are a big footgun for cross-module apps |
22:34 | <rbuckton> | auto-increment gives a meaningful value for enums whose base primitive is number . |
22:35 | <Mathieu Hofman> | Also there's not a way to only freeze prototype right, though I think there is a proposal for that right? |
22:35 | <bakkot> | https://github.com/tc39/proposal-freeze-prototype |
22:36 | <bakkot> | (very stale, as mentioned, just the place to follow along or pick up if you want to make it happen) |
22:37 | <rbuckton> | If you have enum Color of String { Red, Green, Blue } , it can be clear that Color.Red === "Red" .If you have enum Color of Symbol { Red, Green, Blue } , it can be clear that typeof Color.Red === "symbol" and Color.Red.description === "Color.Red" .If you have enum Color of Number { Red, Green, Blue } , what would you expect Color.Red to be? |
22:37 | <Mathieu Hofman> | if you can get someone else to own the breakage fallout bugs on v8, sure |
22:37 | <shu> | good luck |
22:39 | <yulia> | note takers? |
22:40 | <Michael Ficarra> | it's unfortunate, I think there actually is a pattern in use in the wild that we could improve by providing language support with something like this, but this proposal hasn't been motivated in that way |
22:41 | <Michael Ficarra> | I have ideas to tame the override mistake beast, but I need to familiarize myself with previous attempts first |
22:41 | <shu> | i am most interested in danielrosenwasser's agenda item |
22:41 | <shu> | the thing that would convince me is to get rid of a TS wart if TS is willing to do some kind of breaking change, or maybe there's a way to thread the needle, idk |
22:42 | <Mathieu Hofman> | bakkot: you're right, that is the one way it's unlike; and given that it's impossible via the callback, we all decided to accept that difference. but that doesn't mean further avoidable deviation is a good idea return or break being control flow justify the difference |
22:42 | <Rob Palmer> | do we accept that creating a set of unique values for a given type is a frequent use-case? I hope the answer is yes - it feels self-evident based on other languages and my personal usage. |
22:42 | <shu> | sure, but it's not an expressivity gap |
22:43 | <Michael Ficarra> | shu: but I bet there's some value in normalising how that's done |
22:43 | <Rob Palmer> | it's not succinct enough with the current constructs imo - it becomes imperative rather than declarative |
22:43 | <Michael Ficarra> | right now people just do one of a dozen different approaches, and it makes code more accessible if everyone is using built-in support |
22:43 | <ljharb> | i agree on the "coordination" angle for what is currently "a set of strings/symbols/numbers" |
22:44 | <shu> | that feels very hopeful |
22:44 | <shu> | i am not that full of hope |
22:44 | <ljharb> | but i hope we're all on the same page about that motivation before we debate syntax, is all :-) |
22:44 | <Michael Ficarra> | Rob Palmer: not just succinct, but it would be nice if, for example, all of the value-back-to-enum-name functions had the same name |
22:44 | <shu> | but i'm not against it! i just want TS to convince me |
22:44 | <shu> | TS has the biggest lever here in pushing folks to one way |
22:44 | <rbuckton> | I'm interested in formalizing a syntax for a common pattern, just as
|
22:45 | <Justin Ridgewell> | Can we just fix TS's utterly-terrible-no-good-very-bad output for enums? |
22:45 | <Michael Ficarra> | yeah we're going to have to get past that unfortunate typescript cowpath |
22:46 | <Rob Palmer> | Mark is now talking about string unions
|
22:46 | <shu> | people use string literals as enums, don't they |
22:47 | <ljharb> | sure but it's nice if they have identity, so Foo.A and Bar.A aren't accidentally interchangeable |
22:47 | <rbuckton> | You won't necessarily get that for string based enums, but you would for symbol-based ones. |
22:47 | <shu> | i definitely do not want to solve the ADT use case in the same proposal |
22:47 | <shu> | that seems like a very very different problem space to me |
22:47 | <TabAtkins> | Yeah, identity (aka enum members are primitives or similar) is pretty important |
22:47 | <ljharb> | (i'm still bitter from realizing that scala inherits java's silly confusion of chars and ints) |
22:47 | <TabAtkins> | and agree that ADT should be put to the side |
22:47 | <Michael Ficarra> | shu: yes exactly, people use literally dozens of different approaches, and it would be nice if there was more consistency there |
22:48 | <Michael Ficarra> | ljharb: Scala inherits so much of Java's crap, it really kills the language for me |
22:48 | <shu> | Michael Ficarra: i dislike TC39 being looked to as a lever to move the ecosystem one way |
22:48 | <ljharb> | shu: re your queue item, class DID actually kill off almost all of the other ways of doing inheritance (including Object.create , not that it was very popular before that) |
22:48 | <Michael Ficarra> | shu: how is it a lever? people are already doing this all over the place |
22:48 | <ljharb> | so i think an enum syntax would actually achieve that goal. |
22:48 | <shu> | i should qualify, where there is no expressivity gap |
22:49 | <shu> | ljharb: class is good historical evidence, yes |
22:49 | <shu> | thanks |
22:50 | <bakkot> | can't typescript just introduce like a // ts-enum annotation which means "this is an enum" |
22:50 | <bakkot> | without needing new syntax in the language |
22:50 | <bakkot> | I am confused by this whole "it helps static analysis" claim |
22:50 | <ljharb> | "eslint, for normal JS" is still a pretty important use case |
22:51 | <bakkot> | not clear how much this would help eslint? |
22:51 | <ljharb> | targeting parseable grammar (that comes with semantics) is far easier than trying to target a nonstandard commenting convention? |
22:51 | <rbuckton> | There's already a /** @enum {T} */ in JSDoc, but it does not mean what most people want it to mean, unfortunately. |
22:52 | <shu> | shu: how is it a lever? people are already doing this all over the place |
22:52 | <bakkot> | ljharb: if TS wrote down a specific syntax for the comment then it would not be any harder to target |
22:52 | <ljharb> | sure but then it'd still be TS-specific, and that's not relevant to non-TS users. |
22:52 | <Jack Works> | oh sorry I got offlined |
22:52 | <Jack Works> | let me rejoin |
22:53 | <bakkot> | ljharb: I was responding specifically to the "it helps TS analysis" motivation |
22:53 | <bakkot> | if there is some other motivation, sure |
22:53 | <ljharb> | ah ok, gotcha |
22:53 | <rbuckton> | I'm interested in whether ADT enums could help avoid megamorphic ICs in V8, where the "kind" of enum value could be inlined into the IC, so that switching/matching on the enum kind wouldn't be polymorphic. |
22:54 | <Jack Works> | hmm can anyone hear me? |
22:55 | <Jack Works> | looks like still not got online 🤔 |
22:55 | <rbuckton> | The TS compiler has a lot of switch (node.kind) { ... } cases that are megamorphic. Being able to do something like match (node) { when (Node.Identifier) -> ... } and avoid megamorphism would (hopefully) be a perf win. |
22:55 | <Michael Ficarra> | exhaustiveness checks will be a godsend, too |
22:56 | <rbuckton> | Jack Works: Are you able to hear and just can't speak? |
22:57 | <shu> | rbuckton: not sure why a tagged disjoint union "kind", even if built-in, would make a callsite any less megamorphic |
22:57 | <shu> | presumably each disjunct of the sum type still has a different layout |
22:57 | <shu> | if the callsite deals with those different layouts, it's as polymorphic as before |
22:59 | <Justin Ridgewell> | Can anyone give a demonstration of ADT without using pattern matching? |
22:59 | <bakkot> | Justin Ridgewell: every AST |
23:00 | <bakkot> | the usefulness for ASTs means people who work with programming languages tend to think about them a lot |
23:00 | <TabAtkins> | what's the point of adt without pattern matching |
23:00 | <TabAtkins> | (i say, as one of the pattern-matching champions) |
23:00 | <shu> | my hot take is i think PL and compiler people definitely overindex on pattern matching and ADTs |
23:01 | <shu> | like ML is a great language for writing ML compilers |
23:01 | <Justin Ridgewell> | A code example. I don't understand how I get fields out of a Square(int int) |
23:01 | <rbuckton> | Yes, but I'm curious if it would be possible to inline a consistent shared field such as a "kind" that all ADT enum values would be expected to have, so that the switch isn't megamorphic when branching on that "kind". Each branch could then be monomorphic. |
23:01 | <Justin Ridgewell> | The only thing that demonstrates is pattern matching, and I hate pattern matching. |
23:02 | <Ben Newman (Apollo, @benjamn on GH)> | hate? wow |
23:02 | <bakkot> | Justin Ridgewell: normally you name the fields I think |
23:02 | <rbuckton> | Justin Ridgewell: You mean like, square[0] ? |
23:02 | <Justin Ridgewell> | Is it an object? |
23:02 | <rbuckton> | An ADT enum would likely be a record or tuple value, if not a regular object. That's still TBD. |
23:03 | <gkz> | Flow Enums: https://flow.org/en/docs/enums/ Comparison with TS and Flow Enums: https://medium.com/flow-type/typescript-enums-vs-flow-enums-83da2ca4a9b4 |
23:03 | <bakkot> | gkz: your mic is still hot I think |
23:03 | <bakkot> | oh fixed nvm |
23:03 | <Michael Ficarra> | there's a common pattern in Haskell libraries, considered a good practice, to actually not expose your ADT constructors so that consumers can't pattern match on them |
23:03 | <Michael Ficarra> | the idea is you're supposed to do every kind of pattern matching that makes sense as functions and expose those |
23:04 | <Michael Ficarra> | also allows for "smart constructors" |
23:04 | <bakkot> | lol I just did exactly the same thing as PH: https://github.com/Z3Prover/z3/blob/c6539deb6169afde2b569fba89a828f2f726691f/src/api/js/scripts/parse-api.js#L141-L204 |
23:04 | <bakkot> | converts C enums to TS |
23:06 | <shu> | Justin Ridgewell: i don't think you can get fields out of an ADT without pattern matching. the point of ADTs is that you have N type constructors to create the data, and on the other side you have "eliminators" to unwrap the type constructors and get the field out. usually, the eliminator is pattern matching |
23:07 | <shu> | on the flipside, you have classes, which sometimes get described as co-data, where eliminators (methods) are how you define the class to begin with |
23:09 | <shu> | but that part of my life is behind me now, never to be thought of again |
23:09 | <bakkot> | have a hard time imagining going from that to C++ |
23:10 | <Michael Ficarra> | waldemar: I would describe the problem as "there is a common need today to create related but distinguishable values, and there's many different ways to do it. language support would unify these strategies, make code more approachable, provide useful info to tooling to do things like exhaustiveness checks, etc" |
23:10 | <Michael Ficarra> | Justin Ridgewell: you should check out recursion schemes |
23:10 | <bakkot> | that doesn't cover sum types, though |
23:11 | <ptomato> | but the intention was not to cover sum types in this proposal, right? |
23:11 | <bakkot> | ptomato: not clear to me |
23:11 | <bakkot> | definitely sounded like sum types were in scope? |
23:11 | <waldemar> | sum types are the interesting part of this proposal, that we can't currently do in the language |
23:11 | <Michael Ficarra> | the last slide said "simple enums", with other stuff being later |
23:12 | <waldemar> | but they do have a fairly large impedance mismatch given how dynamically typed ecmascript is |
23:12 | <rbuckton> | Michael Ficarra: As well as additional affordances that novel syntax would present, such as the possibilities for constant inlining, decorators, serialization, and consistent patterns for construction (and deconstruction) of these values. |
23:13 | <Michael Ficarra> | I'm frustrated that this couldn't reach stage 1. I think it met the criteria, but just wasn't presented in a way that made that clear, no offense to the champion group. |
23:13 | <Michael Ficarra> | rbuckton: true |
23:14 | <Bradford Smith> | I think what's missing here is some clear examples of the current way of doing what enums do are problematic. |
23:14 | <waldemar> | I'd view the problem more as providing a lightweight statically analyzable way of defining constants in general. But all of the options are biased to do other things by using the enum keyword. |
23:14 | <Bradford Smith> | s/examples of/examples of how/ |
23:15 | <rbuckton> | For example:
Writes
Would be safer at runtime, but would fail to stringify. |
23:15 | <Bradford Smith> | "JavaScript doesn't have enums" isn't a problem all on its own. |
23:16 | <Bradford Smith> | rbuckton: that's a start. I'd love to see a presentation with at least a half dozen such examples and explanation of what the desired behavior is. |
23:17 | <Bradford Smith> | and why it's hard with current language constructs to get that behavior |
23:17 | <sarahghp> |
const Color = { Red: 'red' , ..} |
23:23 | <Jack Works> | 😂 I didn't do a good preparation cause I didn't figured out how to express those ideas in my brain. with suggestions and feedbacks from the chat I think it can be better next time. and as I said in the presentation, I'm not push the proposal forward until it has a complete design and proven to has benefit to add it. I agree that frozen object (or TypeScript/flow.js enum) is enough today (mostly?) so my main focus point is on the ADT enum. |
23:31 | <Rob Palmer> | Jack Works: I'm really pleased you gave this presentation. It seems like there were plenty of people interested in this space. So hopefully they can help you to work more on the use-cases and problem definition. (And I am biased because I love enums) |
23:33 | <Jack Works> | Thanks 🙏 |
23:35 | <gkz> | Jack Works: I would happy to be involved with this proposal if it's something that's going to be moving forward. We had a lot of discussion with both library authors and end users, and examined many use-cases, in preparation for the design of Flow Enums, and then listened and iterated based off of feedback. But again for us a large amount of the value derived is from when enums are used with the type system. We have also begun research on an ADT extension to Flow Enums, and have analyzed use-cases and so on. |
23:39 | <rbuckton> | How is this not solved by { Red: 'red', ... } , but you lose out on that safety. |
23:43 | <Rob Palmer> | Authenticity of enum values (provided by Symbols) is something folk won't reach for unless it has sweet syntax. And I'd argue it's what you normally want for robustness reasons - no typos. |
23:44 | <shu> | enums-as-constants is a different problem than ADT enums |
23:44 | <shu> | until they're separated or more clearly articulated to need to be solved together i'm uncomfortable with the proposal |
23:44 | <bakkot> | symbols also can't be serialized, so I don't think that helps the "would fail to stringify" case? |
23:45 | <shu> | even thuogh Jack Works says ADTs are deferred as a follow on it keeps coming up in the discussion so i'm not sure who's interested in solving what |
23:48 | <Jack Works> | I know rbuckton is interested |
23:50 | <Jack Works> | Ah I should go sleep first, I'm not clear headed now |
23:59 | <rbuckton> | shu: Every time I've brought up enums in discussion with other members of plenary or folks in the TS/JS community, ADTs are invariably mentioned as a feature someone would like to see adopted. The use cases frequently overlap, and a number of languages support enums that can be used both for ADT and non-ADT scenarios. |