05:08 | <devsnek> | surprisingly useful https://gc.gy/56273905.png |
05:09 | <ljharb> | devsnek: tbh that `.spec` thing seems like it'd be a pretty handy standalone npm package |
05:10 | <devsnek> | well its not magic |
05:10 | <devsnek> | it uses the `// #sec-xyz` comment above every function in engine262's source |
05:10 | <ljharb> | ah ok |
05:10 | <devsnek> | i agree though |
05:10 | <devsnek> | i wanted to add something similar in node's repl |
05:10 | <devsnek> | for js stuff and node stuff |
05:10 | <devsnek> | though it would've been mdn not the spec in that case |
05:14 | <ljharb> | i'd want it for the spec :-p |
05:30 | <Bakkot> | new proposal? :D |
05:30 | <Bakkot> | the ultimate reflection mechanism |
05:30 | <Bakkot> | actually I guess to qualify as "ultimate" it would also need to include an interpreter for spec-ese |
05:31 | <devsnek> | Array.prototype.flat.__doc |
05:33 | <Bakkot> | `Set.prototype.add = spec.evaluate(spec(Set.prototype.add).replace('If value is -0, set value to +0', 'If false'))` |
05:37 | <devsnek> | oh gosh |
13:58 | <Sirisian> | littledan, I was looking at the operator overloading proposal. Would it be fair to say a lot of the complexity in the proposed syntax is due to a lack of function overloading? I understand it's stage 1. |
13:59 | <littledan> | I don't understand how function overloading would relate to it. it's true that the proposal's contents are largely about the dispatch semantics. |
14:00 | <littledan> | I don't think you'd want function overloading to behave with the restrictions that operator overloading is defined to have |
14:00 | <Sirisian> | The static kind of design? |
14:01 | <Sirisian> | (I'm not fully clear on the implementation specifics, was just reading the issues). |
14:18 | <Sirisian> | Would it be insane to use a TS-like syntax https://github.com/sirisian/ecmascript-types#classes-and-operator-overloading I'm not really clear on the big picture of what slows things down in V8 and such in regards to operator overloading. Is it being able to define new operators on individual instances that would require lookups and be unworkable? So the operators must be on global class types and all or nothing for all instances? |
14:20 | <Sirisian> | (Also I realize that Symbol doesn't work. I added that a while ago when someone suggested it. Apparently that's a bad idea). |
15:32 | <jmdyck> | Bakkot: so `spec.evaluate(str)` would return a function object that, when called, performs the algorithm steps in `str`? |
15:32 | <Bakkot> | that would be the idea! |
15:32 | <Bakkot> | (to be clear this is not a serious proposal) |
15:37 | <jmdyck> | yeah, it didn't sound entirely serious. Still, I think it'd be possible. |
15:51 | <shu> | Bakkot: i'd feel more comfortable with a spec.evaluate if we first sat down and worked through the meta-machinery |
15:52 | <shu> | ecma261.5, the ecma262 metalanguage |
15:53 | <Bakkot> | working on it |
15:54 | <Bakkot> | michael ficarra and I have a preliminary linter integrated with ecmarkup |
15:54 | <Bakkot> | so we can start enforcing stuff like "variables are defined before use" and so no |
15:54 | <shu> | i'm interested in what the linter thinks about lets-in-branches |
15:55 | <Bakkot> | (that rule isn't implemented yet, to be clear, but should be pretty straightforward) |
15:55 | <shu> | i'm interested in the policy question |
15:56 | <Bakkot> | for now I'd want to start with allowing them, since they're in the current spec |
15:56 | <Bakkot> | and if we eliminate them we can update the linter to enforce that |
15:56 | <Bakkot> | I don't actually mind them much though |
15:56 | <shu> | yeah i like them tbh |
15:56 | <shu> | at least the fairly structured version where two branches both introduce the same alias |
15:57 | <Bakkot> | I think there might be a place or two where there's a "If Foo let X be *" without a corresponding else, and later usages of X also guarded on Foo |
16:04 | <shu> | that also seems fine to me |
16:04 | <shu> | and could be checked by the linter with sufficient sophistication |
16:30 | <devsnek> | littledan: I had an idea about operator overloaded where you try to make it as scoped as possible |
16:30 | <littledan> | devsnek: Yes? |
16:31 | <devsnek> | sort of like `let x = with operator xyz { a + b }` |
16:31 | <devsnek> | this lets it be fairly dynamic |
16:32 | <devsnek> | you can put as much in the block as you want but overloaded operations tend to happen in clumps so they should stay fairly small |
16:33 | <littledan> | devsnek: Would that be a do expression? |
16:33 | <devsnek> | it could be, yes |
16:33 | <littledan> | (I mean, what were you picturing? what else would it be?) |
16:33 | <devsnek> | I haven't formalized this idea that much |
16:33 | <devsnek> | if we have do expressions it would make sense to (ab)use them here |
16:33 | <littledan> | I mean, to me, curly brackets with code in them feel like they should have statements in them |
16:34 | <littledan> | if it's an expression, I'd imagine parentheses |
16:34 | <devsnek> | a + b is an expression statement |
16:34 | <littledan> | right |
16:34 | <devsnek> | so yeah it uses completion |
16:34 | <littledan> | so, it'd work as a do expression |
16:34 | <littledan> | if we're OK with using completion values, that'd be fine. I guess no one has driven this to Stage 2, but it seemed like dherman made good progress last time it was discussed |
16:35 | <devsnek> | but anyway since it encourages small scoping, you can make the protocol more dynamic |
16:35 | <littledan> | hmm, I don't think I follow that part |
16:35 | <littledan> | I think we need to be static just for the integrity goals anyway |
16:35 | <devsnek> | since you're only paying for dynamic behaviour inside the block |
16:36 | <littledan> | "paying for" makes sense when thinking about performance cost, but the integrity things are sort of considered serious either way. The intention isn't that you're opting into chaos mode, but rather into something contained |
16:36 | <devsnek> | integrity? |
16:37 | <littledan> | see "predictability" in https://github.com/tc39/proposal-operator-overloading/blob/master/README.md#design-goals |
16:37 | <devsnek> | hm I think I feel exactly the opposite |
16:37 | <littledan> | hmm how so? |
16:37 | <devsnek> | they should be dynamic and changeable imo |
16:37 | <littledan> | why? |
16:38 | <devsnek> | more javascripty I guess |
16:38 | <devsnek> | that's why I like this language |
16:38 | <littledan> | lots of people have told me specifically, operator overloading shouldn't happen in JS because then it'd be too dynamic and changeable |
16:38 | <littledan> | so, I was trying to respond to that and find something contained |
16:38 | <devsnek> | all I've heard is that dynamic shouldn't happen because of performance |
16:39 | <littledan> | so, I guess I've been a little sloppy about keeping quotes around, but I've personally heard more concerns than just that |
16:39 | <devsnek> | fair enough |
16:39 | <littledan> | let me see what I can dig up |
16:39 | <devsnek> | I really don't like the static stuff though |
16:39 | <littledan> | I mean, for example, ljharb at the last TC39 meeting was saying operator overloading would make JS more confusing, wasn't he? |
16:40 | <devsnek> | I think that's mostly about wide scoping of the overloads |
16:40 | <devsnek> | if you have these narrow scopes it's very obvious when it happens |
16:40 | <devsnek> | although you still have the issue of forgetting to scope |
16:40 | <littledan> | you could also look at the responses here: https://twitter.com/littledan/status/1024958066066898944 |
16:41 | <littledan> | so, i was specifically trying to defend against certain "attacks" against integrity in this proposal. Probably I didn't do a good enough job explaining this in the repo, since people keep asking me the same questions about making more dynamic |
16:41 | <littledan> | so that's something for me to work on |
16:42 | <devsnek> | I think operator overloading works better when it's by intention instead of by operator |
16:43 | <devsnek> | like how in rust you implement deref, not the star operator |
16:43 | <devsnek> | I think people saying "operator overloading is bad" tend along that pathway |
16:44 | <littledan> | hmm, do you have ideas for what that would mean concretely, given the operators we have in JS? |
16:46 | <devsnek> | I don't know if I have a concrete answer |
16:49 | <devsnek> | it occurs to me that people use macros for this in rust, which is almost like my scoping idea |
16:49 | <devsnek> | let x = vec_ops! { a + b } |
16:52 | <littledan> | yeah, I think that's a good thing to look into, but at the same time, I feel like it's appropriate that the operator overloading proposal uses "+" to identify overloading things like a+b |
16:52 | <littledan> | and, I don't think this has to do with the responses to my tweet, which were half about operator overloading making all JS code opaque and confusing |
16:52 | <littledan> | so the concern was on the usage side, not the definition side |
16:53 | <devsnek> | yeah that's what I mentioned above, with small scopes making it clearer where this happens |
16:53 | <littledan> | Sirisian: If we can think of a more intuitive, TS-like syntax, then I'd be all for using it. I was thinking about using decorators somehow, but I was also thinking about avoiding the dependency |
16:53 | <devsnek> | of course if scopes are involved you run into the problem of forgetting to use one |
16:54 | <littledan> | rbuckton has some ideas about nicer syntax too, but I can't figure out how the details would fit together |
16:55 | <devsnek> | could also use some modified operator form |
16:56 | <devsnek> | a @X+ b to add a and b together using X's rules |
16:56 | <Sirisian> | littledan, Do you still work on V8? Was curious. |
16:56 | <littledan> | not really, but my coworkers in Igalia do |
16:57 | <devsnek> | i think you've still got some TODO comments in v8 |
16:57 | <littledan> | devsnek: That wouldn't meet the goals of letting us generalize BigInt and Decimal |
16:57 | <littledan> | yeah, I think I left a lot there... |
16:57 | <littledan> | I codesearched myself the other day and yeah |
16:57 | <littledan> | sorry |
16:57 | <devsnek> | 1n @BigInt+ 1n |
16:57 | <devsnek> | lol you're definitely not the only one who leaves todos |
16:58 | <littledan> | devsnek: By "explain", I mean make it as if BigInt were a feature added through operator overloading |
16:58 | <devsnek> | I'm not sure that's inherently possible |
16:59 | <devsnek> | if we require additional syntax at or around usage sites |
17:16 | <Sirisian> | I'd probably explicitly mention using operator overloading for polyfilling types like you mentioned. Personally I want to polyfill all the types in my proposal. const a = new uint32(5); a /= 2; :O Once thought only possible with BigInt. Truly a paradigm switch. Complex numbers, vectors, and matrices. Should probably just make an explicit list of use cases and examples. |
17:20 | <Sirisian> | Also maybe I missed something, but I didn't understand the a.contents stuff in the code sample. |
18:03 | <Sirisian> | Just looked at it again. I thought it was extending the other library vector when I skimmed it. I get it now. |
18:38 | <jmdyck> | Bakkot: yes there are cases like that ("If Foo let X be *" without a corresponding else, and later usages of X also guarded on Foo) |
22:00 | <littledan> | devsnek: The idea has been that you'd imagine there would be an "operator overloading prelude" for the built-in types. It's an action item I have (from Mark) to actually write this prelude, to prove it's possible. |
22:01 | <devsnek> | is that tangible to user code? |
22:01 | <littledan> | Sirisian: Yes, i've been trying to accumulate use cases, and I'd appreciate your help in that. Feel free to make a PR to the explainer to add more use cases |
22:01 | <littledan> | devsnek: No, in general, you can't reflect over code in JS... how would it be tangible? |
22:02 | <devsnek> | if its not tangible then explaining it doesn't matter |
22:02 | <littledan> | I don't know what you mean by being tangible |
22:02 | <littledan> | maybe I'm not explaining the sense of "explaining" very well... |
22:02 | <devsnek> | js code can observe that there is a prelude and that it contains BigInt and Number |
22:03 | <littledan> | well, you can observe it by doing arithmetic calculations on BigInt and observing that it works rather than throwing a TypeError |
22:03 | <littledan> | that's the exact same way that you'd observe it for your own types |
22:03 | <devsnek> | ok so then again |
22:03 | <devsnek> | there's no point to defining a prelude |
22:03 | <littledan> | the goal is that you can do your own BigInt-like thing, and it'd really work |
22:03 | <devsnek> | because we're talking about behaviour not declarations |
22:03 | <littledan> | not sure what you mean. "defining a prelude" is a design-level exercise |
22:03 | <littledan> | (like writing spec text or explainers) |
22:04 | <devsnek> | i'm not sure how that matches the goal of explaining how bigint works |
22:05 | <littledan> | let's talk about this another time. I don't really know how to get across this concept of "explaining". I'll think about it some more. |
22:05 | <littledan> | "explaining" is a kind of TC39 jargon that predates me in committee; maybe it's not the best word |
22:05 | <devsnek> | i thought it meant representing some behaviour in a way that js code can also interact with |
22:05 | <devsnek> | like toStringTag |
22:06 | <devsnek> | if js code can't interact with the prelude it doesn't matter whether the prelude exists or not, its just some imaginary thing |
22:08 | <littledan> | so, ES6 explained a bunch of things in a way that also added tons of dynamism and reflection. For reasons that I tried to explain, we don't really have the option of adding all that dynamism in this case. But the core of "explaining" isn't dynamism or reflection, but the idea that the thing that's built-in is also something that you could do in user code. That's something real too. |
22:09 | <littledan> | (you might consider things without dynamism/reflection bad, or that this feature isn't worth it, but that doesn't mean that it isn't "explaining") |
22:09 | <devsnek> | to me it is like saying we're adding toStringTag but then not getting rid of [[ClassName]] |
22:09 | <devsnek> | or [[Class]] or whatever it was called |
22:10 | <devsnek> | like the magic is still there, on a separate layer from the stuff js can do |
22:10 | <devsnek> | bigint magically works in any scope and there's no js equiv |
22:10 | <littledan> | Well, there's some things that would have to be pretty special forever, like how + works with strings and non-strings. But I think most of the arithmetic operations should be completely explainable in terms of this general mechanism |
22:11 | <littledan> | BigInt would work with this "prelude" theory. That's the one special case |
22:11 | <devsnek> | if the prelude is invisible to js it doesn't exist |
22:11 | <littledan> | it's a concept, sure. (does anything really exist?) |
22:11 | <devsnek> | no i mean |
22:12 | <devsnek> | bigint is still special |
22:12 | <devsnek> | in the same way |
22:12 | <littledan> | yes, but it's special in just one way; it's not *as* special. That's how I'm defining the design goal, the only way that I can see this matching up with the need to have the operator overloading declarations be scoped |
22:12 | <ljharb> | would the small scope version allow operator stuff to be less static? |
22:13 | <devsnek> | its like if you said "every time a function is invoked, a parallel universe is created where the function is evaluated" |
22:13 | <littledan> | ljharb: In my opinion, no. Do you think it would? why? |
22:13 | <ljharb> | personally the second-class nature of the current operator (and decorators) proposal is a big sticking point for me |
22:13 | <devsnek> | from the perspective of js you could never observe such a thing |
22:13 | <devsnek> | so it doesn't matter |
22:13 | <ljharb> | devsnek: with a dep graph where half of the operators are transpiled and half are native, it'd be observable, no? |
22:13 | <littledan> | you can't observe "use strict", but we stick it in modules implicitly... I'm not really sure what you're talking about, why this is a goal |
22:14 | <ljharb> | which |
22:14 | <devsnek> | what are you talking about |
22:14 | <devsnek> | you can add use strict to code |
22:14 | <devsnek> | that is an observable change to js code |
22:14 | <littledan> | yes, just like adding `with operators from` delcarations |
22:14 | <devsnek> | i don't mean reflection |
22:14 | <devsnek> | i mean what humans using js can observe and interact with |
22:14 | <devsnek> | from that perspective bigint is just as magical |
22:15 | <devsnek> | right now there could already be a prelude with your behaviour |
22:15 | <devsnek> | adding or removing it doesn't change anything |
22:15 | <littledan> | BigInt is built-in. The idea is that it's special in a well-scoped way, limited to just what you could do with a prelude, not that it's *arbitrarily* special |
22:15 | <littledan> | that's the design goal I'm working towards. You can disagree about whether it's a worthwhile goal, but that's what I'm trying to communicate. |
22:16 | <devsnek> | your goal doesn't make sense to me |
22:16 | <littledan> | OK |
22:16 | <devsnek> | i can't judge whether it is worthwhile or not because it appears to me to be a non sequitur |
22:18 | <devsnek> | ljharb: my idea with the scoped version i talked about above is that it would allow more dynamicness |
22:18 | <ljharb> | meaning like, a protocol? |
22:18 | <devsnek> | yeah |
22:19 | <devsnek> | no need for anything static |
22:19 | <devsnek> | but dan said the static-ness was partially because people wanted it to have integrity |
22:20 | <ljharb> | what kind of integrity |
22:20 | <ljharb> | like, how + should be commutative? |
22:20 | <ljharb> | or something else |
22:20 | <devsnek> | i assume it means you can't patch someone's overloads |
22:20 | <devsnek> | which seems rather unjavascripty to me |
22:21 | <ljharb> | unless you've frozen it, i agree |
22:22 | <ljharb> | that's an expectation i have for builtins, but not for user code |
22:23 | <devsnek> | they have configurable: false for toStringTag |
22:24 | <ljharb> | toStringTag is a special (bad) case that i don't see as related to operators |
22:28 | <Bakkot> | devsnek: you can't patch someone's overloads today |
22:28 | <Bakkot> | like you can't redefine what "+" means in my code |
22:28 | <devsnek> | there aren't overloads today |
22:28 | <Bakkot> | so why would... we want to... start letting people do that? |
22:29 | <Bakkot> | I don't think "let other people change the semantics of my code" is really a goal anyone on the committee has |
22:29 | <ljharb> | that's exactly what operator overloading is trying to do |
22:29 | <Bakkot> | no it is not |
22:29 | <ljharb> | let other people define the semantics of my code |
22:29 | <Bakkot> | no? |
22:29 | <Bakkot> | not without my explicitly opting in |
22:29 | <ljharb> | whether people can do it once, or more than once, doesn't seem that different to me |
22:29 | <ljharb> | that's the same as referencing any property on any object tho |
22:30 | <ljharb> | you opt in to using that property, and it's possible it could be changed later |
22:30 | <ljharb> | (when you don't destructure it, obv) |
22:30 | <Bakkot> | and assuming it's not frozen, which in this proposal it would be |
22:30 | <Bakkot> | so hey, all is well |
22:31 | <ljharb> | so you mean like, it's a symbol protocol, but the syntax for declaring it freezes it? |
22:32 | <ljharb> | if so, that does seem reasonable to me; anyone can participate but the default/ergonomic behavior makes it robust |
22:44 | <Bakkot> | I think it also destructures when entering the block, so it's defensive twice |
22:44 | <Bakkot> | don't remember exactly though |
22:44 | <Bakkot> | it was a very early stage design |
22:45 | <Bakkot> | ah, yeah, it reads the Operators property and that thing can only be made by a built-in Operator factory which produces immutable values, cool |
22:46 | <Bakkot> | anyway obviously someone can just define the overloaded functions themselves in a way which has their behavior change over time |
22:46 | <Bakkot> | point is, you have to opt in to using someone else's operators, and random third-party code can't just muck with it |