19:54 | <jschoi> | Could someone familiar with the spec explain https://github.com/tc39/proposal-array-from-async/pull/11#discussion_r719681385 ? How is it that %Array.prototype.values% returns an iterator record? It just returns CreateArrayIterator(O, value) (https://tc39.es/ecma262/#sec-array.prototype.values). |
19:55 | <jschoi> | (Basically, I’m trying to plug the result of CreateArrayIterator into CreateAsyncFromSyncIterator, but the former returns an iterator and the latter requires an iterator record. Surely there’s a way to do this without manually creating a record, right?) |
19:59 | <bakkot> |
It doesn't; it returns an actual iterator (i.e. an object with a (Iterator Records, like all records, are never exposed to user code.) |
20:02 | <bakkot> |
Not that I'm aware of. The normal flow is to call |
20:03 | <bakkot> | (just Let _iteratorRecord_ be the Record { [[Iterator]]: _iterator_, [[NextMethod]]: %ArrayIteratorPrototype.next%, [[Done]]: false }. , I think) |
20:13 | <jschoi> | (just |
20:14 | <jschoi> | I was confused because that’s what zloirock had said it does. It’s good to know that it actually doesn’t; I am no longer confused. |
20:15 | <jschoi> | Oh, wait, I had misunderstood him. He was suggesting using %Array.prototype.values% as the method argument to GetIterator. That should work… |
21:48 | <TabAtkins> | jschoi: In your bind-this, you have some text about the operator "creating bound functions" like .bind() does, but as far as I can tell it doesn't do this. Every example you have is using it immediately as part of a call expression; always randomObj->fakeMethod(x) (equivalent to fakeMethod.call(randomObj, x) ), never randomObj->fakeMethod (equivalent to fakeMethod.bind(randomObj) ). Are your examples too limited, or did the scope change at some point to be a call operator rather than a bind operator? |
21:57 | <jschoi> | jschoi: In your bind-this, you have some text about the operator "creating bound functions" like .bind() does, but as far as I can tell it doesn't do this. Every example you have is using it immediately as part of a call expression; always o->fn is fn.bind(o) ,and o->fn(a) is fn.call(o, a) , because fn.call(o, a) is indistinguishable from fn.bind(o)(a) . |
21:58 | <jschoi> | fakeMethod.call(randomObj, x) is indistinguishable from fakeMethod.bind(randomObj)(x) . |
22:06 | <TabAtkins> | Do you have thoughts about dropping that case in favor of letting PFA + -> -as-a-call-operator handle it? o->fn would instead be o->fn~(...) , which also produces bound functions that record the receiver; the -> here sets the receiver in time for PFA to record it. |
22:07 | <TabAtkins> | Then there's no intermediate object creation when using just -> |
22:13 | <jschoi> | Then there's no intermediate object creation when using just x->o(a) . It can always be optimizable to a direct call without constructing a bound function. There’s no way for the developer to distinguish between Function.bind(o)(a) and Function.call(o, a) , so the engine is free to optimize x->o(a) to use call rather than bind . |
22:17 | <jschoi> | ljharb, rbuckton (OOF til Oct-1), and I have talked a little about how PFA syntax would interact with this use case. PFA syntax in of itself cannot address the call-this use case. But it’s true that it could capture this if o->fn~(…) was given a special case…but that seems more complicated than just making o->fn bind and o->fn(a) call. (And o->fn(a) need not construct an intermediate bound function. It could simply call fn on o directly—because fn.bind(o)(a) is user-indistinguishable from fn.call(o, a) . This hopefully would be an Easy Engine Optimization™. If this optimization turns out not to be Easy, then I would have to reevaluate my approach, but I would also be quite surprised.) |
22:23 | <ljharb> | i am very unoptimistic about the timeline of PFA advancing and would not want to subjugate any other proposal to it at this time. |
22:26 | <jschoi> | FYI, ljharb: bakkot gave some negative signals towards this-bind We might run into some headwinds from the rest of the Committee, too, but we’ll see…I’ll do my best to make its case, though. |
22:28 | <bakkot> | jschoi: if you present the case as it is in the readme, I do not expect you'll be able to get even stage 1 |
22:31 | <ljharb> | i think stage 1 is basically a certainty - it's something we've discussed many times and will do so again - but obviously we wouldn't want to present in a way that would make stage 2 harder to get |
22:31 | <jschoi> | Understood. I’m guessing that you’d block Stage 1? We might need more time than to October to build its case, then…though I need to reinvestigate what the differences between this proposal and HE Shi-Jun’s Extensions or the prior bind-operator proposal are, since this proposal is meant to replace both of them. |
22:31 | <bakkot> | specifically: stage 1 is laying out a problem statement. If the problem statement is "people who want to be defensive against mutation of the global methods find that the standard uncurryThis helper is too slow", I think the committee as a whole - certainly me personally - would not be receptive to that being a problem worth spending more time on |
22:31 | <ljharb> | fair |
22:31 | <bakkot> | if there is a different problem statement, it's more likely you'd get stage 1 |
22:32 | <jschoi> | Would “binding and calling with this is very common and is worth lubricating with syntax” be more compelling to you? |
22:32 | <bakkot> | much more compelling, yes |
22:32 | <bakkot> | I might not agree it's worth new syntax, but that's more of a stage 2 concern |
22:33 | <bakkot> | (that is, a concern for getting stage 2, rather than getting stage 1) |
22:33 | <jschoi> | Right, okay. So I need to focus less on the security aspect or at least subsume it under “bind/call is very common”. Thanks for the feedback. Figuring out what was compelling enough for the prior bind operator and for Extensions to reach Stage 1…although it’s true that neither of them have reached Stage 2… |
22:34 | <bakkot> | (i.e. I agree with the problem statement of "it could be easier to bind this " but am skeptical of the cost/benefit of adding new syntax to solve that problem - syntax is expensive) |
22:35 | <shu> | i am pretty unconvinced this needs syntax |
22:38 | <Justin Ridgewell> | I think anything that aids tree shake ability for bundles is a huge improvement |
22:39 | <Justin Ridgewell> | This and pipelines is how we get nice APIs without resorting to really ugly nested calls |
22:39 | <shu> | i'm missing some steps there. how would syntax for bind help tree shaking? |
22:40 | <jschoi> | It seems that call-this syntax would encourage library developers to create separate “methods” that use this and which are individually importable. For example, RxJS was doing that in v5, although they switched away when it became clear that :: was stuck. |
22:40 | <TabAtkins> | Syntax for call helps there |
22:41 | <shu> | i still don't understand, how does syntax for call help there? |
22:41 | <Justin Ridgewell> | bind doesn't matter for tree-shake, it's just the call syntax |
22:41 | <jschoi> | Example: https://github.com/ReactiveX/rxjs/tree/5.x#es6-via-npm |
22:42 | <TabAtkins> | shu: If you want to write good APIs that look like foo.bar() but with free functions that you can import/tree-shake, you want a call operator so you can do foo->bar() or whatever |
22:42 | <jschoi> |
|
22:42 | <ljharb> | personally i only really need .call syntax, but both bind and call come together so naturally just by including or omitting the invocation parens |
22:42 | <TabAtkins> | (or write them to use their first arg rather than this and use pipe: foo |> bar(^) ) |
22:42 | <Justin Ridgewell> | Tab explains it really well. |
22:43 | <shu> | and bar.call(foo) defeats tree shaking because call is configurable/writable? |
22:43 | <Justin Ridgewell> | But it's anything to avoid the kitchen-sink style object APIs, where they're almost impossible to tree-shake |
22:43 | <jschoi> | Nah, it’s just much less ergonomic. Which is why RxJS moved away from it when :: failed. |
22:43 | <TabAtkins> | No, it defeats "fluent API" |
22:43 | <Justin Ridgewell> | No, that would work, but you'll never see users actually wanting to write that. |
22:43 | <shu> | i... see |
22:43 | <TabAtkins> | baz.call(bar.call(foo)) |
22:43 | <TabAtkins> | that's ugly as sin ^^_ |
22:43 | <shu> | i have no qualms, but okay |
22:44 | <TabAtkins> | versus foo.bar().baz() ? |
22:44 | <shu> | it is uglier than that, yes |
22:45 | <TabAtkins> | Well, we know from wide experience at this point that it's sufficiently ugly that literally nobody does it, and instead they invent .pipe() and start using HOF |
22:45 | <shu> | it is not ugly enough for me to prioritize over applying DCE if that were an impactful optimization for a library |
22:45 | <Justin Ridgewell> | Think of a beginner's perspective, they'll never write baz.call(bar.call(foo)) and it'll be hard for them to understand what's really happening. |
22:45 | <Justin Ridgewell> | foo.bar().baz() is extremely intuitive |
22:45 | <Justin Ridgewell> | My hope is foo->bar()->baz() will be, too. |
22:46 | <TabAtkins> | (I still find .call() hard to understand in anything beyond trivial cases, and I'm far from a beginner.) |
22:47 | <Justin Ridgewell> | I see -> and |> as the next way libraries are written, with the explicit goal of tree-shaking out every unused piece of code |
22:48 | <Justin Ridgewell> | If we start exporting a bunch of top-level functions (with either a this context or a first-param), our libraries can encompass every use case with 0 bloat |
22:48 | <shu> | why even use this |
22:49 | <shu> | why not just do it the old OO-in-C way, and just take the receiver as the actual first argument |
22:49 | <Justin Ridgewell> | Because people are used to foo.bar().baz() |
22:50 | <Justin Ridgewell> | That's the ideal |
22:50 | <Justin Ridgewell> | The closer we can make the final syntax to the ideal syntax, the better adoption we'll see |
22:50 | <shu> | i have a hard time with this because that is an aesthetic i do not share |
22:50 | <TabAtkins> | (I don't think it's that bad, and suspect foo |> bar(^) |> baz(^) will be a significant objection.) |
22:56 | <Justin Ridgewell> | See also firebase's redesign: https://www.youtube.com/watch?v=r5eJQ3nPc6A |
22:56 | <Justin Ridgewell> | They're using first-param, which I think is fine |
22:56 | <Justin Ridgewell> | But I think adoption will be better if we had -> |
22:57 | <shu> | yes, the firebase redesign convinced me of pipe operators' utility beyond catering to those who want more FP in JS, which i categorically don't want |
23:06 | <rkirsling> | yes, the firebase redesign convinced me of pipe operators' utility beyond catering to those who want more FP in JS, which i categorically don't want |
23:10 | <rkirsling> | on topic though, the thought of -> really makes me feel like "oh no we're turning into Perl" |
23:10 | <rkirsling> | ...except it's worse, because foo-> isn't accessing something related to foo 😓 |
23:12 | <jschoi> | If I remember my Perl correctly, foo->bar() is a method call, right? That’s actually pretty similar… |
23:13 | <TabAtkins> | (that's also a PHP method call ^_^) |
23:13 | <rkirsling> | er well, hmm, yeah |
23:14 | <rkirsling> | I guess my last sentence isn't quite right |
23:14 | <rkirsling> | but it does feel like promoting something unusual in a way that makes very basic things confusing |
23:16 | <jschoi> | I can understand that. this , bind, and call are all pretty confusing in JavaScript.But they’re all still very common, too; they’re core parts of JavaScript. So it still may be worth lubricating them. Jordan’s global-protection use cases were what spurred me to actually make this proposal, and it’s impossible without the proposal (due to Function mutation)…but I’m sure it’s only a tiny subset of what people use bind and call for in general. |
23:18 | <jschoi> | I suppose since arrow functions’ premiere, bind and call became much less prevalent… |
23:18 | <rkirsling> | right but these things are longwinded because they're not the things beginners do |
23:19 | <rkirsling> | I would be GENUINELY scared at making every beginner worry about "was it . that I'm supposed to write? but there's also -> ..." |
23:19 | <rkirsling> | ...which was my first reaction to needing to edit a Perl script |
23:24 | <jschoi> | rkirsling: That’s understandable. Would you have similar concerns about the old :: bind operator and the Extensions proposal, too? |
23:25 | <rkirsling> | I don't believe so |
23:25 | <rkirsling> | I feel like -> is a very charged symbol |
23:25 | <rkirsling> | in other words, I'm not opposed to a solution here |
23:27 | <jschoi> | I had waffled between &. , !. , and :: . Jordan gave me feedback that it shouldn’t contain . because of confusion with . . I’m not a huge fan of :: because it implies namespacing to me, and o::fn() doesn’t seem a lot like namespacing to me (it didn’t belong to o ; it’s being dynamically bound to o ). But, well, the operator token is bikesheddable, and I’m open to any suggestions that would reduce concerns. |
23:28 | <jschoi> | I’ll probably change the token, and I’ll reformulate the explainer to focus on “call and bind are very, very common (and that includes but isn’t limited to code that wants to use globals robustly)”, and then hopefully that’ll be enough for at least Stage 1. |
23:49 | <bakkot> | I am annoyed that tree-shaking is such a powerful argument here. it's not like it's actually that difficult to tree-shake out unused methods, especially with typescript, it's just that no one is doing it. |
23:49 | <bakkot> | if I had more time I would just fixup webpack or rollup to do it properly and then we could just use the existing language |
23:50 | <bakkot> | it seems dumb to change the language just because the tooling is bad. |
23:53 | <Justin Ridgewell> | if I had more time I would just fixup webpack or rollup to do it properly and then we could just use the existing language |
23:58 | <bakkot> | Justin Ridgewell: lucky someone else already wrote one then! |
23:59 | <bakkot> | but also, you can actually get quite far without a type system |