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>

How is it that %Array.prototype.values% returns an iterator record?

It doesn't; it returns an actual iterator (i.e. an object with a .next method). Maybe you have this question backwards?

(Iterator Records, like all records, are never exposed to user code.)

20:02
<bakkot>

Surely there’s a way to do this without manually creating a record, right?

Not that I'm aware of. The normal flow is to call GetIterator, which does the work for you. But manually making the record should just be a one-liner, so I don't think it's worth abstracting it out.

20:03
<bakkot>
(just Let _iteratorRecord_ be the Record { [[Iterator]]: _iterator_, [[NextMethod]]: %ArrayIteratorPrototype.next%, [[Done]]: false }., I think)
20:13
<jschoi>
(just Let _iteratorRecord_ be the Record { [[Iterator]]: _iterator_, [[NextMethod]]: %ArrayIteratorPrototype.next%, [[Done]]: false }., I think)
Got it, thanks.
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 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?
The examples are too limited. 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 ->
My current understanding is that no object creation has to occur with 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 ->, especially if its primary use case is global protection (https://github.com/js-choi/proposal-bind-this/issues/8).

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>
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { map } from 'rxjs/operator/map';

Observable::of(1,2,3)::map(x => x + '!!!'); // etc
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
you know I gotta ask whether that pun was intended
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
Requiring a full type system to do this makes it incredibly difficult.
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