01:01 | <ptomato> | another heads up for delegates, about Temporal. we got another spec bug report from two implementors today, and while investigating that one, Justin found another spec bug. I've added the fixes to the slides that I will present on Wednesday, but again since they are late additions, we understand if delegates will need more time to consider them. |
03:05 | <jschoi> | Was there no CoC Committee update in the first session? It was on the agenda, but I didn’t hear any update and it’s not in the notes. |
08:43 | <sarahghp> | No, I think the plan was to have it later. |
08:57 | <Rob Palmer> | The Halloween edition of TC39 plenary will begin in 3 mins. |
08:59 | <Surma> | 👻 |
09:03 | <nicolo-ribaudo> | I forget it every time: I don't have to fill the form every day, right? |
09:04 | <Rob Palmer> | correct, nicolo - we keep the same password |
09:04 | <Rob Palmer> | we just need each attendee to fill it in once |
09:05 | <nicolo-ribaudo> | Ok thanks! |
09:10 | <sarahghp> | (I had to do it twice because if you don't open matrix in a new window or save the info, you can't get back to it 🙈) |
09:16 | <Tierney Cyren> | oh hi Surma |
09:16 | <Surma> | 👋 Tierney Cyren |
09:17 | <Tierney Cyren> | glad to see you here :) |
09:18 | <devsnek> | 👋hello |
09:20 | <bakkot> | for the notes: frank said 3 PRs, but I can't figure out what the third one was |
09:20 | <bakkot> | anyone know? |
09:20 | <rbuckton> | That looked like it worked. |
09:20 | <bakkot> | there was the non-continuous weekend one and the annex A one and then a third one |
09:21 | <ryzokuken> | "canonical"? |
09:21 | <bakkot> | ah yes thanks |
09:23 | <devsnek> | queue may be confused https://gc.gy/285b32a5-6cd1-4c10-9e9a-8a300f754897.png |
09:26 | <devsnek> | an engine could probably do nice stack traces like that itself, no need to include pos in the error |
09:30 | <devsnek> | shu: raw returns some magic object that tells json.stringify to use the string as source instead of a string |
09:30 | <rbuckton> | In the cases where I have needed position information in JSON, I've generally had to rely on something like the TypeScript parser and AST, even in a purely JS project. My use cases so far have been fairly niche, such as doing minor updates to an existing JSON file while preserving the source formatting (usually for JSON files intended to be both human and machine readable such as package.json). |
09:39 | <bakkot> | syg: right now, JSON.stringify of an object without a toString is guaranteed to do the thing you expect absent a reviver, but if the symbol is shared that's no longer true |
09:46 | <bakkot> | Michael Ficarra: I am strongly opposed to having the marker be an object with a symbol here because it implies that the engine will need to do a lookup on every object |
09:46 | <bakkot> | which is observable |
09:46 | <bakkot> | it's simpler in the sense of "fewer kinds of object in the language", true, but not in terms of "how much observable behavior there is", and the second thing seems like the thing we should be minimizing most of the time |
09:48 | <Michael Ficarra> | bakkot: fair point |
09:49 | <shu> | that's a good point |
09:49 | <shu> | though i don't think a particular problem for performance in the common case |
09:49 | <Michael Ficarra> | bakkot: how do we ensure these exotic marked objects don't usefully escape the callback? |
09:49 | <devsnek> | you don't |
09:49 | <Michael Ficarra> | is the value of the slot fresh? |
09:50 | <devsnek> | oh you mean per-invocation |
09:50 | <devsnek> | in terms of spec language you could say its {[[Index]]: n, [[String]]: s} |
09:50 | <devsnek> | and expect that index back |
09:50 | <devsnek> | or you could expect the whole object i guess |
09:50 | <devsnek> | cuz it has identity |
09:52 | <bakkot> | Michael Ficarra: you can't ensure they don't escape, but like devsnek says you have a field which indicates which invocation it is |
09:52 | <bakkot> | s/field/internal slot/ |
09:52 | <shu> | syg: right now, JSON.stringify of an object without a |
09:52 | <devsnek> |
|
09:52 | <Michael Ficarra> | okay I mean a counter is just a fresh value, so I'm fine with that |
09:52 | <shu> | like, wouldn't the new guidance be "here's another hook we need, if you hook into it, well, you get hook behavior" |
09:52 | <bakkot> | and stringify encounters an object with this slot holding a different value than for the current invocation it can throw |
09:53 | <devsnek> | what happens if you call raw multiple times in one callback |
09:53 | <Michael Ficarra> | devsnek: same index |
09:53 | <devsnek> | i feel like that should work, you could compose a sub-object |
09:53 | <nicolo-ribaudo> | Tbh I would expect the argument to raw to always be a string |
09:53 | <nicolo-ribaudo> | Otherwise it's not raw |
09:53 | <devsnek> | no i mean like |
09:54 | <devsnek> | (key, value, { raw }) => ({ a: raw(foo), b: raw(bar) }) |
09:54 | <nicolo-ribaudo> | uh ok, that makes sense |
09:55 | <nicolo-ribaudo> | For example to represent a class Fraction { #numerator: bigint; #denominator: bigint } |
09:55 | <devsnek> | ye you could put a tojson on that |
09:55 | <devsnek> | which uses raw |
09:56 | <bakkot> | toJson wouldn't get passed raw |
09:56 | <bakkot> | I think |
09:56 | <bakkot> | unless we do the global well-known symbol approach |
09:56 | <devsnek> | why does it matter which approach |
09:57 | <nicolo-ribaudo> | Can't toJSON receive a parameter even if it's not global? |
09:57 | <devsnek> | i think tojson should get to participate |
09:57 | <nicolo-ribaudo> | Can't toJSON receive a parameter even if it's not global? |
09:57 | <bakkot> | I mean it could but it doesn't in the current proposal |
09:57 | <bakkot> | also if we're going to let toJson participate I think a well-known symbol would make more sense anyway |
09:58 | <bakkot> | since the point of having the per-invocation value is to ensure the stringifier callback has control |
09:58 | <bakkot> | and toJson is for giving the objects being serialized control |
09:59 | <bakkot> | though, I guess a global well-known symbol means we'd be imposing a cost on all existing serialization calls, which seems bad |
10:01 | <shu> | extra get per object in the graph to be stringified does seem bad |
10:01 | <shu> | though isn't that an issue even with a per-stringify symbol |
10:02 | <bakkot> | yeah |
10:02 | <bakkot> | not with an opaque object though! |
10:03 | <shu> | well, not an extra get but an extra slot check, which ought to be much cheaper, yes |
10:03 | <bakkot> | so, ok, here is an alternate possible design which I don't hate: add a new |
10:03 | <bakkot> | then toJson can use this function, and revivers can as well |
10:04 | <bakkot> | basically just the "allow it to be not per-invocation" approach, but with internal slots instead of symbols |
10:04 | <shu> | +1 from me. if the opaque object has gensym behavior it also complicates the performance of the slot check |
10:05 | <bakkot> | "has gensym behavior"? |
10:05 | <devsnek> | https://gc.gy/d9ff7617-8eb7-4039-81bf-ac0201805137.png |
10:05 | <shu> | the fresh minting behavior where each invocation of stringify has a new kind of opaque object only valid for that invocation |
10:06 | <devsnek> | gensym = generate symbol? |
10:06 | <shu> | yeah |
10:06 | <shu> | it's the scheme thing that returns fresh symbols |
10:09 | <bakkot> | I know what gensym is but am confused about how it affects GC here |
10:10 | <bakkot> | oh, unless you're thinking about the GC of the underlying string? |
10:10 | <bakkot> | I would just not worry about the fact that holding the opaque object holds the string, I guess |
10:10 | <shu> | no not gc |
10:11 | <shu> | i meant the "is this one of them opaque objects" checks |
10:11 | <bakkot> | oh |
10:11 | <bakkot> | I mean, you check if it has the slot, and then if it does you check if the value for the slot matches the value for the current invocation |
10:12 | <bakkot> | seems not so bad |
10:12 | <bakkot> | and it's just the presence-of-slot check which gets paid by code not using this capability |
10:14 | <shu> | agree, seems not so bad |
10:15 | <Michael Ficarra> | yeah I favour this raw function that returns an object with an internal slot with index (or two such slots) option now |
10:15 | <bakkot> | github issue: https://github.com/tc39/proposal-json-parse-with-source/issues/19 |
10:16 | <bakkot> | Michael Ficarra: https://github.com/tc39/proposal-json-parse-with-source/issues/18#issuecomment-951789801 |
10:16 | <shu> | Michael Ficarra: what index? the per-invocation counter? |
10:16 | <shu> | ehh, i'd still rather we just have global |
10:17 | <rbuckton> | The only issue I see with an exotic object is if you write any custom JSON serialization logic today in a replacer/reviver, you would need to be able to test whether the object is one of these exotic objects so you can pass it through rather than trying to convert it yourself. |
10:17 | <Michael Ficarra> | yes, per-Json-stringify counter and possibly a per-callback counter |
10:17 | <shu> | i am against a per-callback counter |
10:17 | <Michael Ficarra> | that's probably fine shu |
10:18 | <shu> | i am also against a per-invocation counter and favor bakkot's concrete alternative, to be clear |
10:18 | <shu> | but i am more against per-callback counter |
10:18 | <rbuckton> | So you need both a JSON.rawSting and a JSON.isRawString , otherwise it comes harder to write serializers/deserializers. |
10:18 | <devsnek> | what do you do with isRawString |
10:19 | <rbuckton> | If you do custom serialization of any kind you need to know if the object you get in your custom serializer/replacer is this special exotic object or some other object. |
10:20 | <nicolo-ribaudo> | fwiw, in the R&T repository there has been a bunch of discussion about tagged strings (https://github.com/tc39/proposal-record-tuple/issues/258#issuecomment-947115796). This feels similar, since it's a string tagged with (we are not looking to include tagged strings in the R&T proposal) |
10:20 | <devsnek> | why do you need to know |
10:21 | <devsnek> |
|
10:22 | <rbuckton> | A naive replacer might do:
|
10:22 | <rbuckton> | If there's no way to test, you have to expect to pass through objects with no keys, which adds complexity. |
10:23 | <bakkot> | rbuckton: JSON.isRawString = o => o && typeof o === 'object' && !(toJSON in o) && !['{', '['].includes(JSON.stringify(o)) |
10:23 | <Michael Ficarra> |
|
10:23 | <Michael Ficarra> | if we do that, we'd have to at least have a testing function like what Ron's talking about |
10:23 | <Michael Ficarra> | but then you force anyone using this feature to test anything that goes into their returned object |
10:23 | <bakkot> | rbuckton: isRawString enforces that its argument is a primitive JSON string or digit sequence) |
10:24 | <rbuckton> | The upside of a symbol is its something you can check for. |
10:24 | <bakkot> | (to be clear, not saying this is great, just saying that it's possible-ish without a new function) |
10:24 | <nicolo-ribaudo> | rbuckton: JSON.rawString("{}") would be observabily the same as Object.freeze({ __proto__: null }) |
10:24 | <rbuckton> | bakkot: So, your suggestion is to JSON.stringify an arbitrarily complex/large object that may have its own nested toJSON just to check for an exotic raw string object? |
10:24 | <bakkot> | nicolo-ribaudo: "assuming that isRawString enforces that its argument is a primitive JSON string or digit sequence" |
10:25 | <nicolo-ribaudo> | nicolo-ribaudo: "assuming that isRawString enforces that its argument is a primitive JSON string or digit sequence" |
10:25 | <bakkot> | rbuckton: you can add Object.keys(o).length === 0 if you're worried about that |
10:25 | <rbuckton> | It would have to be much more robust, checking for null prototype, no properties, etc. |
10:26 | <rbuckton> | bakkot: More like Object.getPrototypeOf(o) === null && Object.getOwnPropertyNames(o).length === 0 && Object.getOwnPropertySymbols(o).length === 0 etc. |
10:27 | <bakkot> | unless you're concerned about proxies, I don't think you need those additional checks |
10:27 | <bakkot> | no non-exotic object can have the behavior that toJSON is absent and also !['{', '['].includes(JSON.stringify(o)[0]) |
10:27 | <rbuckton> | In which case I point you at Array.isArray , which is better (and more reliable) than o => typeof o === "object" && o !== null && Object.prototype.toString.call(o) === "[object Array]" |
10:28 | <rbuckton> | I thought rawString could return a string that contains any valid JSON. Couldn't you do JSON.rawString("{}") , and wouldn't that fail the check? |
10:29 | <bakkot> | rbuckton: "assuming that isRawString enforces that its argument is a primitive JSON string or digit sequence" |
10:29 | <bakkot> | which I think it should, separately from this concern |
10:29 | <bakkot> | (otherwise you can abuse it to add whitespace, which seems bad) |
10:29 | <rbuckton> | That's fair, but you're still requiring a fairly complex check that could be more easily exposed by the language. |
10:29 | <bakkot> | yeah |
10:30 | <bakkot> | like I said, not saying my proposal is great, just that it's possible-ish |
10:30 | <bakkot> | though I guess you do also need to check for boxed numbers / strings, which is also annoying |
10:30 | <bakkot> | so yeah it seems reasonable to add isRawString |
10:31 | <rbuckton> | An isRawString shim is much more complex than an isArray shim, which means its much easier to get wrong if you're rolling your own serializer (which, I would argue, is the exact situation you're in if you're using this functionality). |
10:32 | <Tierney Cyren> | is there a good way for me to get involved in the LF discussion meetings? |
10:32 | <ryzokuken> | Tierney Cyren: you may join this ad-hoc group |
10:32 | <Tierney Cyren> | I've wanted to participate but how is unclear and I've not gotten around to asking |
10:32 | <ryzokuken> | write an email to Patrick and he'll add you to the mailing list/event |
10:33 | <ryzokuken> | talk to Isabelle, I think she's responsible for getting everyone interested from MS involved |
10:46 | <Tierney Cyren> | $4m seems like a lot but what's the actual runway that provides? |
10:46 | <Tierney Cyren> | that number does not feel helpful in a vacuum |
11:59 | <Rob Palmer> | we are starting up plenary in 1 minute! |
12:06 | <devsnek> | it also happened with NativeError.prototype.toString lol |
12:06 | <devsnek> | chrome had that from 2016 to 2019 until i removed it |
12:09 | <Michael Ficarra> | instead of tightening host restrictions, can't we just make a section of test262 which is like "okay to fail, but should probably be looked at because it's more likely a bug"? |
12:10 | <devsnek> | i was just thinking that |
12:10 | <devsnek> | like a common-sense directory lol |
12:10 | <shu> | Michael Ficarra: yes, i plan to bring that up |
12:10 | <shu> | i object to this proposal |
12:10 | <Michael Ficarra> | thanks shu |
12:10 | <devsnek> | what is this proposal |
12:10 | <shu> | but i'll wait for jordan to actually say the proposal first |
12:10 | <ljharb> | i haven't made it yet. |
12:10 | <shu> | before i add myself to the queue |
12:11 | <devsnek> | i assume its restricting names on prototypes or smth |
12:11 | <Michael Ficarra> | test262-parser-tests has a "fail" directory which is spiritually the same: https://github.com/tc39/test262-parser-tests/tree/master/fail |
12:12 | <ljharb> | "Any property on a given object mentioned in the specification, must ONLY appear in the locations specified on that object or its prototype chain" |
12:12 | <devsnek> | test262-parser-tests has a "fail" directory which is spiritually the same: https://github.com/tc39/test262-parser-tests/tree/master/fail |
12:13 | <Michael Ficarra> | hey you try naming files with random non-programs |
12:13 | <Michael Ficarra> | pull requests welcome |
12:14 | <ryzokuken> | are the names just a hash of the source code or sth? |
12:14 | <Michael Ficarra> | yep ryzokuken |
12:14 | <devsnek> | i like test262 names |
12:17 | <bakkot> | man I gotta update that project |
12:17 | <ryzokuken> | test-assignment-really-terribly-bad.js |
12:19 | <bterlson> | would we need work in e.g. test262.report if we add a new test category? |
12:21 | <devsnek> | test262.report is bocoup right |
12:21 | <bterlson> | Yeah |
12:23 | <bterlson> | I wonder if there are test cases we would like to add to 262 aside from things we feel bold enough to tighten that would nonetheless be useful for impls? I seem to recall tests in this category back in the day but have forgotten |
12:23 | <devsnek> | gc tests :P |
12:26 | <bterlson> | devsnek: you ready to present extending null next? |
12:26 | <devsnek> | yep |
12:26 | <devsnek> | assuming i haven't fallen asleep by then |
12:27 | <bterlson> | do not fall asleep 😁 |
12:27 | <Mathieu Hofman> | gc tests :P cleanupSome ). I have on my todo list of fixing all these |
12:28 | <devsnek> | i'm still sad about cleanupsome being separated |
12:28 | <bterlson> | Justin Ridgewell: if we need to pull in items today, are you available to present destructure private fields? |
12:38 | <erights> | NativeError.prototype.toString on v8 only shadows Error.prototype.toString uselessly |
12:51 | <yulia> | hm i didn't really have a position |
13:02 | <yulia> | lol ok i am going to be the only one maybe |
13:05 | <rbuckton> | Could super() just be a no-op in class extends null ? |
13:05 | <bakkot> | yulia: someone brought up the possibility of class extends void {} for a statically-analyzable version of null |
13:06 | <shu> | she brought that up, yeah |
13:06 | <bakkot> | oh yeah |
13:06 | <shu> | i guess some SM engineers thought it odd because (void 0) is undefined? |
13:06 | <bakkot> | sorry I'm tired |
13:06 | <yulia> | yeah |
13:06 | <shu> | that doesn't seem like a very big objection, though |
13:06 | <shu> | the void 0 thing |
13:06 | <yulia> | yeah i think it works kind off well personally |
13:06 | <ljharb> | it's super weird to not allow extends X for any valid X, and to force null to have a special syntactic form |
13:07 | <ljharb> | null objects aren't supposed to be special, from a conceptual standpoint |
13:07 | <bakkot> | null was already special |
13:07 | <bakkot> | you have to extend a constructor |
13:07 | <bakkot> | there is nothing weird about null not working |
13:07 | <yulia> | i would consider null special |
13:07 | <bakkot> | it was always going to be special-cased |
13:07 | <rbuckton> | Besides, you can't write class extends void 0 since a void expression isn't a LHS expression |
13:07 | <devsnek> | the weird thing is that we special cased it to not throw on definition |
13:08 | <yulia> | if we can do this from a syntax perspective, i don't mind if super == this |
13:08 | <yulia> | thats fine |
13:08 | <rbuckton> | We could always have a shared %NullConstructor% that you assign as the base for class extends null so that super() could work. |
13:11 | <rbuckton> | i.e., change 8.f.ii. of ClassDefinitionEvaluation to be Let constructorParent be %NullConstructor%. , where %NullConstructor% is a built-in function purely for the purpose of supporting super() . |
13:11 | <shu> | yulia: could you expand a bit on the runtime problem in SM? |
13:12 | <yulia> | basically, we won't be able to determine which bytecode we need to use until we execute it |
13:12 | <shu> | bytecode for what operation? |
13:12 | <yulia> | for the look up of the prototype iirc |
13:13 | <yulia> | im trying to find the notes just a sec |
13:13 | <shu> | thank you |
13:13 | <yulia> | "In this proposal if the base is null, the spec is asking for the constructor to still extend Function.prototype (which is reasonable enough). Normally we pull the super target function off the constructors own prototype while running, but in this case we also need to check elsewhere to find the original heritage to know if it was null vs explicitly Function.prototype. This can be fixed for us by adding the original heritage (or at least a flag) to the JSFunction of the constructor. Problem is we don't really have a lot of spare slots or flags on functions themselves" |
13:14 | <ljharb> | Mathieu Hofman: it's not a base class unless it inherits from Object. |
13:14 | <ljharb> | perhaps base classes should all have extended null, and extends Object should have been required to make something inherit from Object.prototype, but that ship's long gone |
13:14 | <shu> | ooh, i see |
13:14 | <shu> | that makes sense, yes, that's a bigger problem, in its requiring two bits of info |
13:17 | <ljharb> | (totally fine with me if the constructor itself also inherits from null) |
13:17 | <bakkot> | the custructor should not inherit from null |
13:17 | <rbuckton> | (totally fine with me if the constructor itself also inherits from null) .bind |
13:17 | <bakkot> | that makes the function prototype methods not available |
13:18 | <shu> | yes gut reaction for not having F.p methods on the constructor is "oof" from me |
13:18 | <ljharb> | Function.prototype.bind.call etc, which is already what robust code does |
13:18 | <devsnek> | do people use function prototype methods on constructors? |
13:18 | <bakkot> | ljharb: no code does that |
13:18 | <ljharb> | lol most doesn't, sure |
13:18 | <bakkot> | your code, my code, a couple of polyfills, and no one else in the entire world |
13:18 | <ljharb> | but how often are you binding constructors either |
13:18 | <bakkot> | not never! |
13:19 | <shu> | ⊤ class |
13:19 | <bakkot> | that's Object |
13:19 | <bakkot> | or, well |
13:19 | <shu> | a second top class |
13:19 | <bakkot> | i guess we don't really have a lattice |
13:20 | <devsnek> | TIL bound functions proxy [[Construct]] |
13:20 | <shu> | create your own! |
13:21 | <ljharb> | yulia: if the word extends appears then it's not a base class |
13:22 | <devsnek> | haha shu i was just thinking about structs |
13:22 | <ljharb> | a base class is class {} which includes inheriting from Object |
13:22 | <shu> | that is not the usual OO understanding of "base class" |
13:22 | <devsnek> | yulia: if the word |
13:22 | <ljharb> | i don't care what the spec says about what's base/derived, i'm concerned with what users think |
13:23 | <yulia> | i feel like this isn't a useful direction for the discussion? there are many ways to read it and virtual classes are really useful |
13:23 | <ljharb> | and the syntax determines that imo, not theoretical inheritance models |
13:23 | <yulia> | so, i am happy to consider other syntax |
13:23 | <yulia> | like, very much -- we don't have to have extends at all |
13:23 | <shu> | and the syntax determines that imo, not theoretical inheritance models |
13:24 | <ljharb> | if that were broadly true then there would have been outcry that default class inheritance wasn't null |
13:24 | <ljharb> | Object.prototype isn't a "top class" because you can have things that don't inherit from it |
13:24 | <rbuckton> | a base class is defined in relation to a class definition. In class B extends A {} , A is a base class in that B derives from A . B could always be the base class for another definition (i.e., class C extends B {} ). |
13:25 | <shu> | i meant more like "top of a lattice" |
13:25 | <rbuckton> | You can say that B is a subclass, and that class A {} is not a subclass |
13:25 | <shu> | ron's right on general use of "base class", ime |
13:25 | <ljharb> | i suspect i'm not familiar with that term in the way you're using it (lattice) |
13:25 | <bakkot> | strong agree with shu here |
13:26 | <Mathieu Hofman> | a base class is defined in relation to a class definition. In |
13:26 | <jschoi> | 🌱 class {} |
13:26 | <jschoi> | [joke] |
13:27 | <yulia> | I am going to drop in 3 min, and caroline will take over for mozilla |
13:27 | <yulia> | so, any further questions on the null stuff, just dm me and ill get to it when im back |
13:30 | <shu> | ljharb: "root class" is just as cromulent for what i mean, the thing hierarchies top out at |
13:31 | <shu> | like, surely it's not just syntax, because we set up class hierarchies before class syntax was added |
13:31 | <shu> | and people understood them just fine |
13:33 | <ljharb> | sure but in those cases null and a constructor were 100% interchangeable |
13:33 | <ljharb> | you did have to either omit or conditionally call a superclass constructor, to be fair, but there wasn't a syntactic requirement around super there |
13:38 | <bterlson> | rbuckton: if we need will you be able to bring forward a regexp item to today? |
13:39 | <devsnek> | Uint8Array.fromAsync(reader) 👀 |
13:39 | <Mathieu Hofman> | I personally do not think it's weird to have 2 ways to express syntactically a root class: class {} or class extends void {} . In both cases no super() would be allowed |
13:40 | <ljharb> | a root class doesn't extend anything. |
13:40 | <ljharb> | root class {} or something would at least not have that confusion |
13:40 | <rbuckton> | bterlson: That would be fine |
13:40 | <Mathieu Hofman> | My understanding is that in english void is nothing ;) |
13:41 | <ljharb> | void produces undefined which is a reified thing, not the absence of a thing |
13:41 | <devsnek> | technically in english void and null are synonyms |
13:41 | <Mathieu Hofman> | no, void expr produces undefined |
13:41 | <bakkot> | class {} does not literally "not extend anything" either - it extends Object.prototype |
13:41 | <ljharb> | right, currently that's the only way JS supports using void |
13:41 | <bakkot> | but, the way you think about it is that it does not extend anything |
13:41 | <ljharb> | bakkot: yes, that's true as well. but it's not explicitly claiming to not extend |
13:44 | <bakkot> | my point is that " on the other hand, colloquially it is understood that |
13:44 | <devsnek> | i would do null class Foo {} |
13:45 | <ljharb> | const o = null { a: b } when |
13:45 | <shu> | there's already proto syntax tho |
13:45 | <devsnek> | wait hold on |
13:46 | <devsnek> | class Foo { __proto__: null } |
13:46 | <devsnek> | what does that do |
13:46 | <ljharb> | with the colon, it's a syntax error, in TS i'm not sure |
13:46 | <devsnek> | oh wait it uses DefineOwnProperty |
13:46 | <rbuckton> | Its a SyntaxError :) |
13:46 | <ljharb> | __proto__ = null would probably create a public class field, or throw, not sure |
13:46 | <bakkot> | can someone put in a conclusion for the previous agenda item in the notes |
13:46 | <devsnek> | man i really wish class fields used Set |
13:46 | <rbuckton> | In TS its says you have a field named __proto__ whose type is null . |
13:47 | <bakkot> | I am so glad class fields do not use Set |
13:47 | <ljharb> | (re conclusion) there really isn't one i think, it's an update |
13:47 | <shu> | galaxy brain is: what if object literals used set |
13:47 | <bakkot> | they used to |
13:47 | <devsnek> | lol |
13:47 | <devsnek> | ok so we just need to special case |
13:47 | <devsnek> | __proto__ = null in classes |
13:48 | <bakkot> |
~ ES3 |
13:48 | <devsnek> | is that SSA |
13:49 | <rbuckton> | Except that already has a well-defined behavior:
|
13:49 | <shu> | bakkot: wtf |
13:49 | <devsnek> | ya ik :( |
13:50 | <rbuckton> | Just need decorators:
|
13:50 | <bakkot> | shu: yeah it was a wild time |
13:50 | <Michael Ficarra> | rbuckton: doesn't matter if nobody relies on it |
13:50 | <devsnek> | i need to convert more people to my function decorator cult |
13:50 | <Michael Ficarra> | devsnek: I'm always willing to try a new cult |
13:51 | <rbuckton> | i need to convert more people to my function decorator cult |
13:51 | <devsnek> | that kind of hoisting is almost never used in practice |
13:51 | <devsnek> | i don't know a case where its needed |
13:52 | <rbuckton> | Function expression decorators: ✔️ Function declaration decorators: ☠️ |
13:52 | <devsnek> | i seriously believe we can do decorators on function declarations by just not hoisting the function value |
13:52 | <rbuckton> | that kind of hoisting is almost never used in practice |
13:52 | <devsnek> | no one will notice |
13:52 | <bakkot> | i definitely hoist a lot of functions |
13:52 | <bakkot> | on the other hand, decorators are bad, so I'm ok with them being harder to use :P |
13:52 | <devsnek> | but do you call them before their declaration |
13:52 | <devsnek> | or just refer to them |
13:52 | <bakkot> | devsnek: yes |
13:53 | <bakkot> | call them |
13:53 | <devsnek> | ok but |
13:53 | <devsnek> | you can just |
13:53 | <devsnek> | not put decorators on that code |
13:53 | <bakkot> | functions go at the bottom, logic goes at the top |
13:53 | <bakkot> | (for scripts) |
13:53 | <devsnek> | its a simple choice |
13:53 | <rbuckton> | At one point I was thinking about this:
|
13:54 | <rbuckton> | as a shorthand for const f = function () {} |
13:54 | <devsnek> | ron i won't accept stylistic practice from the typescript compiler source |
13:54 | <bakkot> | const function means a different thing |
13:54 | <devsnek> | until you get rid of the 40k line file |
13:54 | <rbuckton> | |
13:55 | <rbuckton> | Either way, I'm all for function expression decorators. |
13:55 | <devsnek> | anyway i think this would work in practice and i encourage some company that has resources to do research to research it |
13:55 | <rbuckton> | Just not a fan of adding a decorator breaking hoisting on a function declaration. |
13:56 | <devsnek> | i don't think it counts as "breaking" as much as a tradeoff, it certainly doesn't have any impact on existing code |
13:56 | <rbuckton> | since it also can break circular imports |
13:56 | <devsnek> | it can't break circular imports |
13:56 | <bakkot> | re: the actual presentation, I strongly agree with WH |
13:56 | <bakkot> | sqrt is good, we should have it |
13:56 | <devsnek> | i mean it can break stuff but that's unrelated to the circular importing |
13:57 | <rbuckton> | Its the same hoisting problem. Hoisted functions are reachable in a circular import. |
13:58 | <devsnek> | right i'm just saying the base case is unchanged and the worst case is the same case as an undecorated class or a variable decoaration |
13:58 | <rbuckton> | i don't think it counts as "breaking" as much as a tradeoff, it certainly doesn't have any impact on existing code |
13:58 | <devsnek> | its the same hazard as migrating function to class |
13:59 | <rbuckton> | There's a hidden hazard with decorators that doesn't exist when you're changing function -> class , in that its still function . |
14:00 | <ljharb> | adding a decorator to something definitely risks all sorts of breakage; it's not something you can expect to do transparently |
14:00 | <rbuckton> | If someone is using TypeScript (or JS with the TS language service), we could give you errors on decorated function decls. |
14:00 | <ljharb> | you could do that when it breaks hoisting too |
14:01 | <ljharb> | (not arguing for breaking hoisting, necessarily, but none of those are compelling arguments against doing so to me) |
14:01 | <devsnek> | p sure the two most popular eslint configs yell at you about hoisting and circulars, separately |
14:02 | <devsnek> | anyway that's my hill |
14:03 | <devsnek> | honestly i don't even know what i would use decorators on classes for lol |
14:03 | <devsnek> | and yes i know there are use cases, that's just where i'm at personally |
14:03 | <ljharb> | i very much want
to be a thing |
14:03 | <rbuckton> | honestly i don't even know what i would use decorators on classes for lol |
14:04 | <rbuckton> | I want function decorators, but I also want parameter decorators. |
14:04 | <devsnek> | i just want @router.get('/foo') function getFoo() {} |
14:04 | <ljharb> | why that over router.get('/foo')(function getFoo() {}) ? |
14:05 | <devsnek> | because that's ugly and weird |
14:05 | <devsnek> | and it doesn't compose well |
14:06 | <devsnek> | @get('/foo') @loginRequired({ admin: true }) @ratelimit(4, 1) function getFoo() {} |
14:06 | <ljharb> | |> ? |
14:06 | <rbuckton> | One reason:
|
14:06 | <devsnek> | lol |
14:06 | <ljharb> | lol fair, but that's not compelling to me; if you want a name don't use arrows |
14:06 | <devsnek> | i do not like expression position decorators lol |
14:06 | <ljharb> | i will always believe FNI was a mistake |
14:07 | <rbuckton> | ljharb: You want @bound , let the snek have @dec function f() {} . |
14:07 | <Michael Ficarra> | FNI was a mistake only if you think name is anything other than a debugging facility |
14:07 | <ljharb> | lol |
14:08 | <ljharb> | FNI was a mistake only if you think name is anything other than a debugging facility |
14:08 | <bakkot> |
oh god, this is the strongest argument against function decorators I've yet seen |
14:08 | <devsnek> | just have a code server that supports fni |
14:08 | <bakkot> | if people are going to write code like that we should not have function decorators |
14:09 | <devsnek> | like what |
14:09 | <rbuckton> | The VS Code codebase uses so many parameter decorators for DI, we really need parameter decorators shortly after class/member decorators. |
14:09 | <devsnek> | oh |
14:09 | <devsnek> | bakkot: have you never used flask in python |
14:09 | <bakkot> | devsnek: yeah, it's awful |
14:09 | <devsnek> | its incredible |
14:09 | <devsnek> | lol |
14:09 | <rbuckton> | Parameter decorators are so much higher on my list of priorities than function decorators, tbh. |
14:09 | <bakkot> | especially debugging other people's flask apps |
14:09 | <bakkot> | just instantly unmaintaintable nightmares |
14:10 | <devsnek> | idk i love it |
14:10 | <devsnek> | aside from the globals |
14:10 | <rbuckton> | Worst case, you can do:
|
14:10 | <devsnek> | should've been function parameters |
14:15 | <shu> | at a deep level i really do not get dependency injection |
14:17 | <devsnek> | i do not like DI |
14:29 | <shu> | but what is it? |
14:29 | <shu> | is it just passing parameters or is there some gestalten design thing i'm not getting |
14:30 | <jschoi> | Yeah. It’s just passing parameters. |
14:32 | <devsnek> | but then you need parameter decorators for some reason |
14:34 | <jschoi> | It’s kind of like the opposite of import in that…if A needs B and B needs C—instead of B pulling in specifically C for itself—B defines a parameter, A pulls in B and C, and A plugs C into B’s parameter. |
14:35 | <jschoi> | So A can switch C with something else compatible with B’s parameter before plugging it into it. |
14:35 | <rbuckton> | most DI is something like taking parameters for your dependencies, but the actual construction of your object graph is handled by a composer. |
14:36 | <rbuckton> | It allows you to substitute dependencies for testing or even at runtime based on circumstance. |
14:38 | <jschoi> | Whenever we need to refer to another variable elsewhere, we always face the choice of (1) defining it as a function parameter to be plugged in later or (2) importing it and hardcoding the dependency. There’s nothing special about that choice; it’s just an everyday thing. |
14:40 | <jschoi> | Although like Ron said, some people use elaborate composer systems that try to abstract away this plugging in for you. |
14:40 | <rbuckton> | DI using parameter decorators is something like:
|
14:41 | <rbuckton> | And in test code, you can do:
|
14:43 | <rbuckton> | Of course, main.js is a contrived example. Many DI containers are fairly flexible, allowing you to specify optional dependencies, aggregate dependencies, nested containers, disposable containers (to manage component lifetime), etc. |
14:43 | <rbuckton> | As well as lazily-injected circular dependencies using field decorators. |
14:54 | <shu> | and why would i want to do this? |
14:56 | <rbuckton> | You would want to do this if you're a very large application that wants to break itself into smaller components for ease of maintenance, or support pluggability/extensions. |
14:58 | <rbuckton> | Its a fairly common idiom across any number of programming languages. Its primarily used server-side or in desktop applications, though not so much in the browser. |
14:58 | <shu> | the easiest to maintain thing is the thing i can read |
15:00 | <rbuckton> | Like anything, once you understand the system its fairly easy to read. |
15:08 | <rbuckton> | The VS Code system has a decorator factory that is used to create the actual decorators used for injection, and names the decorators to coincide with the interface name of the expected input. As a result, your code looks something like (in TS):
|
15:10 | <rbuckton> | All the decorator does is provide the glue that the composition plumbing uses. The idea is fairly similar to context providers in React. I don't have to dig around in the plumbing of my application to find and use a dependency, I merely need to declare that I require the dependency and the composition runtime will either provide it, or fail to compose early because of an unsatisfied dependency. |
15:11 | <rbuckton> | Its also much like a module import graph, except that you have more control over lifetime of the entire dependency graph. |
15:12 | <rbuckton> | The Managed Extensibility Framework (MEF) API in .NET is another example of a DI system, and it actually uses Import and Export attributes to define dependency relationships within a DI container. |
15:17 | <sarahghp> | this seems like an invitation to write unnecessarily complex code ... the virtue of low-magic is links are really clear (I also don't really like context in React tho) |
15:18 | <sarahghp> | to be verbosely clever instead of code-golf-style tersely clever |
16:01 | <rickbutton> | does anyone have a good resource for descriptions of non-standard JS extensions over the years? as-in, pre-harmony stuff that got removed like conditional catch / let blocks / etc |
16:03 | <rickbutton> | rubber duck method, i guess this is a good resource: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features |
16:07 | <annevk> | Doesn't list E4X! |
16:07 | <jschoi> | Microsoft JScript had a special :: syntax for events, I believe. (CC: rbuckton) |
16:08 | <Jack Works> | And JScript supports `expr() = expr2`? |
16:10 | <rickbutton> | what does that do? |
16:11 | <jschoi> | Golly, I do not remember. I wish there were still resources about this. 😔 |
16:12 | <Jack Works> | what does that do? |
16:13 | <nicolo-ribaudo> | what does that do? |
16:15 | <rickbutton> | wild |
16:16 | <rickbutton> | eval("x") = 2 is what engine developers fear |
16:23 | <nicolo-ribaudo> | Ok, maybe it wasn't for eval: the ES 1 specification, at paragraph 11.2.3, says that any host function can return a reference, but `eval` doesn't |
16:26 | <rbuckton> | You can do (x) = 2 today, but you can't do ({ x }) = { x: 2 } . |
16:28 | <rbuckton> | not a non-standard extension, just an idiosyncrasy of the language |
16:29 | <nicolo-ribaudo> | We are taking about `fn(x)=2`, not `(x)=2`! |
16:48 | <ljharb> | can anyone read through https://tc39.es/ecma262/#sec-number.prototype.toexponential and confirm whether 0.0.toExponential(2) should be '0.00e+0' vs '0e+0' ? |
17:05 | <shu> | uh, looks like 0.00e+0 |
17:06 | <shu> | f is 2, so step 9.a says m is "000" |
17:06 | <shu> | then step 11 puts a . after the first digit |
17:14 | <ljharb> | ok cool, thanks - that seemed right to me but i wanted another check |
17:51 | <bakkot> | re: f() = x , see discussion in https://github.com/tc39/ecma262/issues/257 |
18:01 | <bakkot> | on some previous occasion this came up, bterlson said it was vbarrays specifically that used this |
18:02 | <bterlson> | yessir, vbarrays |
18:02 | <bakkot> | and someone in the meeting chat said:
|
18:02 | <bakkot> | though I never did manage to find an example of this I could actually get to work in IE6 |
18:02 | <bakkot> | maybe that was too new though |
18:03 | <bterlson> | I think because of the COM magic, it's possible vbs wouldn't work right on newer systems even if IE6 and JScript+JS did |
18:03 | <bakkot> | oh, fun |
18:03 | <bterlson> | I recall there being some particular system setup when I was testing VB/JS interop |
23:57 | <ptomato> | hi chairs, re. https://github.com/tc39/Reflector/issues/396#issuecomment-952406138, chipmorningstar needs a power-up 😄 |