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 |
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. |> ? 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 |
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> |
|
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> |
|
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 For what it’s worth, |
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 |
14:32 | <jschoi> | Then :: 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 Are you saying you would expect it to group as |
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 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 People already do things like bracketed receiver-first style “fluently” anyway, like with |
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. |