00:18
<jschoi>
ljharb: Got it, thank you. I will not float Function.unThis as an alternative possibility to bind-this/call-this.
Although I will direct the bikeshedding over bind/call-this’s syntax, I will try to push rec :> f(arg0) as my favored syntax, since rkirsling and Justin Ridgewell are also fine with it.
00:18
<jschoi>
I will also try to be persuasive to MM and company that both pipe and bind-this deserve to be in the language (“big frequency × big clunkiness = worth improving with syntax”).
00:18
<jschoi>
I wonder if it would be more persuasive if we dropped the binding semantics without arguments (making rec :> f an error)…That would get rid the overlap with PFA syntax and pipe operator.
00:19
<jschoi>
We could always add binding semantics later. And, as people have pointed out, most binding involves extraction from the same original owner object as the receiver, which requires clunky repetition in rec :> rec.f anyway.
00:20
<ljharb>
i am perfectly content to not have the binding form, but also it seems practically zero-cost to have, so I’m not sure why we’d drop it
00:20
<Justin Ridgewell>
I personally want a call syntax, and don’t care at all about the binding ability
00:21
<Justin Ridgewell>
** a receiver-first call syntax
00:23
<jschoi>
Because some other representatives (definitely TabAtkins, hence their call-this syntax idea…maybe yulia | sick during January plenary?) have expressed concerns about new proposals overlapping with each other – and the desire to minimize overlap between features where possible. So this may make it more palatable in that sense.
00:23
<jschoi>
We did resolve in the post-plenary meeting, “In general, some overlap is okay, but too much is bad; we have to decide this on a case-by-case basis.”
00:24
<jschoi>
In this case, it seems to be “not really much loss, not really much gain, maybe punt to later”. I don’t have strong opinions on this issue myself, other than whatever it takes to garner the most support within Committee…
00:25
<jschoi>
Persuading MM about bind-this’s worth is going to be important, because if we cannot persuade MM to not block bind-this, then JHD will block pipe operator…In the situation that we have, we must have both or neither.
00:25
<ljharb>
sure, agreed. As long as i have one or the other syntax, in proper receiver-first order, I’m happy
00:26
<jschoi>
Just to clarify: You had expressed in the January post-plenary meeting that you do greatly prefer bind-this (receiver first) to call-this (function first), but even call-this would be acceptable enough for you not to block the pipe operator. That’s still true, right?
00:31
<ljharb>
Yes, but i also don’t think call-this holds its syntax weight without receiver-first
00:31
<ljharb>
we already have .call for that
00:32
<ljharb>
call-this would provide the robustness i want, but without the aesthetics/ergonomics it deserves, iow
00:32
<jschoi>
.call is still clunky; I’d make a conciseness argument there, especially for such a frequently used feature of the language.
00:34
<jschoi>
If we drop binding from r :> f(), then it no longer would make sense to call it “bind-this”.
Therefore I plan to start calling the proposal as a whole the “this” operator (“call-this”?), and we can continue to distinguish r :> f() versus f.@(r) with “receiver first” versus “function first”.
00:52
<jschoi>

Justin Ridgewell: You’ve expressed before the desire for object-oriented programmers to use less prototype methods and more individually exported functions. This is a big reason why you have supported adding the pipe operator. It’s part of a bigger shift in the ecosystem, e.g., with Firebase’s new JS API.

However, if both the pipe operator and the “this” operator get added to the language…do you anticipate yourself or other programmers individually exporting this-based functions for use with the “this” operator – rather than non-this-based functions for use with the pipe operator?

00:52
<jschoi>
I ask this because I anticipate viral ecosystem schism to continue being a (weak?) concern from Waldemar and maybe Tab and others.
00:53
<jschoi>
We concluded that this concern was relatively weak in the post-plenary meeting, but it’s probably going to get brought up again…
01:16
<jschoi>
TabAtkins: If we drop function binding from rec :> fn(), then the overlap with PFA syntax disappears. Is it okay if I call this new proposal “receiver-first call-this”, and change your idea to “function-first call-this”? I’m asking since you have first claim to the “call-first” name.
01:17
<TabAtkins>
Yeah that's fine, I have no attachment to the proposal except as a way to thread the syntax needle
02:21
<Justin Ridgewell>
Yes, I'd prefer this-based functions, because I think the IDE integration for a receiver first is considerably better.
02:22
<Justin Ridgewell>
Eg, imagine you already have a const foo = new Foo()
02:23
<Justin Ridgewell>
If I do foo :> b (and I just typed b), and IDE could predict functions which receive a Foo instance
02:30
<jschoi>
Yes, I'd prefer this-based functions, because I think the IDE integration for a receiver first is considerably better.
Do you anticipate that IDEs will add support for function suggestions after |>? I can certainly see that happening…
02:30
<jschoi>
This IDE-integration advantage for this-based functions unfortunately somewhat weakens my argument. But I anticipate that the IDE advantage is going to be temporary.
02:30
<Justin Ridgewell>
I think both will receive IDE support
02:31
<jschoi>
This IDE-integration advantage for this-based functions unfortunately somewhat weakens my argument. But I anticipate that the IDE advantage is going to be temporary.
…That is, it weakens my argument that the ecosystem-schism risk is low.
02:31
<Justin Ridgewell>
But the pipeline operator's won't be as efficient because it could be any function that has those starting letters
02:31
<jschoi>
It would have to infer the typing of the pipe head in order to be able to filter them, yes. Though I’m not sure if that’s any different than having to infer the type of the expression before :>.
02:31
<Justin Ridgewell>
With bind-op/call-op, it can narrow it down to functions with those starting letter that also use this type as the recevier
02:32
<Justin Ridgewell>
The |>'s argument can go anywhere
02:32
<jschoi>
Ah, I see, yes.
02:32
<Justin Ridgewell>
##.prop access, fn(##) first arg, fn(1, 2, ##) third arg, etc
02:33
<Justin Ridgewell>
With call-op, we know it's the recevier
02:35
<jschoi>
Okay, so with that, I think I’m going to have difficulty arguing that :> won’t encourage an ecosystem schism between libraries that export functions that use this and libraries whose functions do not use this
02:36
<jschoi>
The best I may be able to argue might be: “Even if libraries appear that export functions that use this—which is already possible without :>—it would not be a big deal, because, when you have both |> and :>, you can fluently switch between the two styles in a flow of data by mixing |> and :>…just like how you can already mix |> and ..”
02:36
<Justin Ridgewell>
I think the schism already exists with prototype-based methods and free functions in pipeline?
02:37
<Justin Ridgewell>
If anything, this is closer to the way programmers write code in prototype-based APIs.
02:37
<jschoi>
The argument, I believe, is that, while it already exists, it may worsen it by encouraging further creation of individually exported this-based functions (which are not common right now).
02:38
<jschoi>
Though I could try to argue that, even if libraries in that style may become more common, fluency would not be impacted when you can mix both |> and :>…That’s the best argument I got now.
02:39
<jschoi>
import { a, c, e, f } from 'A'; 
import { e } from 'B'; 
a.b |> c(^^).d :> e() |> f(^^).g;
02:43
<Justin Ridgewell>
You're missing a context token in the f().g
02:43
<jschoi>
Fixed. Anyways, A there is a library exporting functions that don’t use this, and B is a library that exports functions that do use this.
02:44
<jschoi>
That’s the schism that Waldemar et al. are concerned about—that libraries like B would be encouraged, as you yourself would create—but maybe it would still be okay, since we can mix |> and :> in dataflows…?
02:46
<jschoi>
(Yes, this is similar to the ecosystem schism between ordinary . prototype method calls versus non-this-using function calls, but one could argue that this existing schism is already “bad”…and that compounding it by making the third category—non-prototype this-using functions—more common…would make the situation even worse. So I imagine they might say.)
02:48
<Justin Ridgewell>
I understand and sympathize a bit, but I think it's warranted in this case.
02:49
<Justin Ridgewell>
The upside here for extremely efficient tree-shaking is massive
02:50
<Justin Ridgewell>
The similarity with class-based approaches make ease-of-use pretty good
02:51
<Justin Ridgewell>
Without the "ideal" syntax here, I worry that we won't reach that point
02:51
<Justin Ridgewell>
Even with pipeline (which has a huge ergo benefit)
02:52
<Justin Ridgewell>
The backwards ordering and topic token are enough friction where people may continue to use prototype-based.
02:52
<Justin Ridgewell>
I would say that pipeline's ability to support receiver-first-arg style functions is the odd one out
02:53
<Justin Ridgewell>
That's the unneeded overlap
02:53
<jschoi>
Receiver-first-arg style functions?
02:54
<Justin Ridgewell>
Instead of function foo(this: Bar) {}, accepting Bar as the first param: function foo(rec: Bar) {}
02:56
<jschoi>
Ah, functions that do not use this and which instead use arguments only. Well…surely you are not saying that all functions that do not use this are non-idiomatic, are you? There are plenty of such functions in JavaScript core alone, like console.log or parseInt. Or with things like Lodash.
02:58
<jschoi>
We already have this schism between functions that use this and functions that do not…and the pipe operator’s ability make the latter more fluent is crucial.
02:59
<jschoi>
In fact, I daresay most newly written functions nowadays might not use this at all…
03:00
<Justin Ridgewell>
I think the standard lib only has a few first-arg like that
03:00
<Justin Ridgewell>
Most APIs are class based, and there's a huge difference when using a method vs a first-arg function
03:00
<Justin Ridgewell>
I should say, I think most APIs are class based
03:01
<Justin Ridgewell>
The jump from method to first-arg is large enough that I don't think pipeline is really solving for it.
03:01
<Justin Ridgewell>
Like, it does it because it falls naturally out of the expression based format we chose for pipeline
03:02
<Justin Ridgewell>
But it's not really revolutionizing the way we call functions
03:02
<Justin Ridgewell>
(single-depth functions, nested functions are vastly improved)
03:02
<jschoi>
Well, it allows us to make nested function calls into linear flows. Which we are arguing is a big deal.
03:02
<jschoi>
Yeah, and with functional dataflow you always have nesting.
03:03
<jschoi>
Well, so what I’m seeing is that some people are going to be set on this-based functions. And, yes, the pipe operator does not help them much, because |> blah.call(^^) is still a big pain.
03:03
<jschoi>
In that case, ecosystem schism with :> might be inevitable.
03:03
<jschoi>
The best way that I see to assuage concerns about ecosystem schism, then, is to argue that schism already exists and would be improved with :> and |> together.
03:05
<jschoi>
That |> alone does not sufficiently solve the problem for developers, because it does not much help this-based functions, and many developers will still gladly use this-based functions…and they are not going away.
03:05
<jschoi>
…So, if mixing between |> and :> is inevitable, then is there a way to better harmonize |> (a very loose operator) and :> (a very tight operator similar to .), I wonder…?
03:05
<jschoi>
a.b |> c(^^).d :> e() |> f(^^).g :> i()
03:07
<jschoi>
I can hear people saying, “It’s confusing that we have both |> and :>. Do I need to use ^^ with :>? How does the grouping work?” Can we harmonize this better…?
Is it really good that we are making :> look like |>? Would it be better to make it look like .? (I know that rkirsling would much prefer the former.)
03:17
<rbuckton>
I'm not sold on an infix operator for this, it seems too ripe for confusion. I.e., how do I pass this to foo().bar()? To which call is it passed? What if I want to pass it to the other one?
03:18
<rbuckton>
I still generally favor something like foo(this: x).bar() or foo().bar(this: x) because it's explicit and unambiguous.
03:21
<Justin Ridgewell>
I think that's a prettier syntax than the foo@(x)
03:21
<jschoi>

I do present foo(this: x).bar() as one of the options in https://docs.google.com/presentation/d/1-MLGCibETPX8NiIvNJ1xOxiMS-NB8GCbDGNcB5patiU/edit?usp=sharing.

For what it’s worth, x :> foo().bar() would be (x :> foo()).bar().

03:21
<Justin Ridgewell>
But doesn't help with the ergo that I'm looking for
03:23
<jschoi>
I had read “ergo” as “therefore” at first…In the end, we’re all looking for that ergo in life.
03:24
<Justin Ridgewell>
Lol. I mean ergonomics
03:24
<jschoi>
Yeah, it took me a while but I got it in the end. 😅
06:42
<ljharb>
i don't think passing the receiver inside the argument parens is clean, simple, or acceptable
06:43
<ljharb>
you'd do x :> foo().bar(), i'd expect, because it applies to the last one in the chain. if you wanted to pass a receiver to foo, you'd do (x:> foo()).bar().
06:46
<rbuckton>
Then :> seems a confusing choice of sigil as it can't chain with . and it's too close to |>, which can chain.
06:49
<rbuckton>
And your intuition about which call gets the this is the opposite of mine, which is exactly my point. In-argument-list makes more sense to me because that's how you do it today in JS: foo.call(x).bar(), or foo().bar.call(x).
11:35
<Justin Ridgewell>
you'd do x :> foo().bar(), i'd expect, because it applies to the last one in the chain. if you wanted to pass a receiver to foo, you'd do (x:> foo()).bar().
It has to be the opposite for exactly the reason Ron says.
14:32
<jschoi>
Then :> seems a confusing choice of sigil as it can't chain with . and it's too close to |>, which can chain.
I think this is a real concern. I switched from :: to :> because of rkirsling’s concerns about confusion with . and with other languages’ ::, but now the precedence seems weirdly mismatched with |>.
14:33
<jschoi>

Wait, right now, the operator’s RHS still must be an identifier, a chain of identifiers, or a parenthesized expression—like decorator expressions.

Right now x :> foo().bar() groups as (x :> foo()).bar(). Because it’s basically a slightly looser version of ., which is very tight.

Are you saying you would expect it to group as x :> (foo().bar())?

14:34
<jschoi>
…Maybe I should add a syntax-precedence bikeshed slide to the slideshow…but I don’t know how the RHS syntax would even work with |>-like loose precedence.
14:34
<jschoi>
(I note that bracketed receiver-first style would not suffer from precedence problems: the grouping of something like x![foo]().bar() is clear and explicit.)
15:10
<ljharb>
And your intuition about which call gets the this is the opposite of mine, which is exactly my point. In-argument-list makes more sense to me because that's how you do it today in JS: foo.call(x).bar(), or foo().bar.call(x).

the way you already do it with .call is the problem. We need a way where the receiver appears before the function, just like in OOP chaining. in-arg-list doesn’t solve that problem, it just adds one of the weirder things in TS. Arg lists are for args, only.

which sigil we use or what precedence it has isn’t important to me, and we can bikeshed that all we like.

15:25
<jschoi>

ljharb: You have expressed before that you dislike bracketed receiver-first style, like x![foo]().bar(). Can you elaborate on this: Do you dislike it as much as you dislike function-first style or this-argument style?

People already do things like bracketed receiver-first style “fluently” anyway, like with x[Symbol.iterator]().next()

15:42
<ljharb>
x![foo] looks like I’m accessing a foo property on x, which I’m asserting is non-nullish
15:42
<ljharb>
i believe that’s precisely what TS interprets that as
15:43
<ljharb>
(a property referenced by the name in the variable foo, ofc)
15:43
<ljharb>
square brackets are for computed properties, whether definition, access, destructuring, etc.
15:45
<jschoi>
Yeah, I agree, square brackets are for computed properties…but, in a sense, though, calling a function on a receiver x![foo]() can be seen as using it as a computed “quasi-property”, as if it were a function property that belonged to the receiver like x[foo](). I think that analogy could be conceptually useful. (Though I am more concerned about the clearer grouping.)
15:47
<jschoi>
It doesn’t have to be x![foo] either. It could be x#[foo] or x~[foo].
15:59
<ljharb>
i really don’t think it’s clear with the brackets. That primarily implies it’s a property on the object to me - that it will set the receiver is incidental.
16:00
<ljharb>
the goal here isn’t to “pretend it’s a property on the object”, because that’s not how this works, that’s just one of the justifiably confused mental models some folks have
16:42
<Richard Gibson>
shouldn't receiver-first vs. function-first matter much less in a world where pipelines make inversion easy, e.g. nodes |> Array.prototype.at@(^^, -1) or nodes |> Array.prototype.at(this: ^^, -1)? I think the dominant concern ought to be syntax burden, particularly factoring in the effects of binding precedence upon [mandatory] parenthesization in plausible expressions.
16:55
<ljharb>
just because pipelines are one way to do it doesn’t mean they must be the only way.