| 18:56 | <jschoi> | Ah, yeah, one of Ben Lesh’s threads. I mentioned him earlier as someone who was so vehemently against Hack pipes that he seemed to become disillusioned with TC39 more broadly. He’s…somewhat of an outlier in the intensity of that reaction. I still don’t really understand why he places so much weight on tacitness of the primary argument, relative to linearization, to the point of wanting to shut the whole thing down. And I still haven’t seen a persuasive articulation from him of why tacitness matters that much more than linearization for fluency. I’m a fan of his work, but he genuinely seems not to see much value in linearization on its own, and I still don’t think I’ve seen a clear explanation from him as to why. That said, it’s certainly possible that others will also feel emotionally ambivalent or opposed to Hack pipes’ explicitness. And that is a reason for some nervousness. |
| 19:02 | <jschoi> | Your “how would you phrase [their problem statements]” question is addressed to ljharb, but, for what it’s worth, when I put call-this’s proposal together, I relied on statistical uses of .call, .bind, and .apply from Gzemnid (see issue #12).I hadn’t seen anyone make the connection between call-this and Reflect.apply until you did. I would love to hear more about how call-this would help Reflect.apply. Maybe it would be worth adding to the explainer. |
| 19:02 | <jschoi> | (And, yes, pivoting call-this into “this-sensitive functions attached to prototypes are poorly tree shakeable, so we should encourage standalone this-sensitive functions” is possible…as long as we accept that that might kill pipe operator in the Committee, due to their overlap in the dataflow use case.) |
| 20:02 | <jschoi> | Pipes would incentive plain functions at least a little, if not a lot. The magnitude is uncertain, but I think it certainly would be a positive effect (a little or a lot…for me, personally, a lot). People like writing and reading x.f0(a).f1(b).f2(c), and they don’t like writing or reading f2(f1(f0(x, a), b), c). So they choose to make f0, f1, and f2 prototype methods and not standalone functions.The same people might like reading x |> f0(##, a) |> f1(##, b) more than f2(f1(f0(x, a), b), c).Those people might even like reading it enough to choose to make f0, f1, and f2 plain functions instead of prototype methods, where they wouldn’t have done it had it remained a choice between only x.f0(a).f1(b).f2(c), and they don’t like writing or reading f2(f1(f0(x, a), b), c).But would the difference be enough to cause a significant increase in plain function usage? Nobody can claim to be confident about the whole community; we can only make our best judgements. I certainly think it would make plain function usage nicer. Of course, like you said, pipe operators have benefits other than incentivizing DCE-friendly plain functions. But Hack pipes do “nudging the ecosystem towards compatible functions”; the debate shouldn’t be about whether they nudge developers toward plain functions at all, but rather about how much they would. |
| 20:03 | <Richard Gibson> |
Basically, all of https://github.com/tc39/proposal-call-this/blob/main/README.md#:~:text=There%20are%20a%20variety%20of%20reasons%20why%20developers%20use%20.call and half of https://github.com/tc39/proposal-call-this/blob/main/README.md#call-is-clunky are already addressed by
It specifically remedies the subject–verb separation of
that's much better than And regardless, I'm pretty much of the opinion that a data-flow perspective on call-this doesn't even make sense. Even the above |
| 20:04 | <jschoi> | I’m a little confused by this. expr |> ##.method() is almost always worse than expr.method(), except in the rare case you would need to parenthesize expr in (expr).method(). |
| 20:21 | <jschoi> | Thanks, this is helpful. |
| 20:23 | <jschoi> | Yes, Reflect.apply does have several advantages over Function.prototype.call, including improving subject–verb separation. The real debate would probably be over if their use cases are compellingly common enough to warrant syntax—as I have tried to argue to the Committee on behalf of Jordan, with not much success. |
| 20:23 | <jschoi> | That SVO word order has empirically not been compelling enough to cause widespread use of Reflect.apply-using callThis helper functions—this point is well taken, although I’m sure other factors are involved in that too. |
| 20:25 | <jschoi> | Your point on the dataflow overlap between call-this and pipe operator being maybe overblown is also well taken. This was certainly the conclusion drawn in the first place when pipes reached Stage 1 in 2021: Jordan raised concerns that advancing pipe would kill any future bind operator because of overlap, and others responded that pipes are now orthogonal to any bind operator and would not kill it. This was the overlap-minimizing approach I’ve tried to take with both pipe and call-this. (I haven’t made it a secret that, if I had to choose one, I would rather have pipe than call-this, but I have tried to make the Committee fairly consider call-this, on its own non-dataflow merits, without considering any overlap with pipe operator.) |
| 20:25 | <jschoi> | I am confused about the notion that “calling a method on a class instance gets the same pipeline DX benefits as calling a bare function”, but I’d rather discuss that in another thread in which I’ve already asked about this (https://matrix.to/#/!mjlgwjKxWUpgSgeCQU:matrix.org/$uoU7N_56TqMXsv8rCwzYtUnf25u64_8LKbr-z-Zvg9w?via=matrix.org&via=igalia.com&via=mozilla.org). |
| 20:59 | <Evan Winslow> | I can't tell exactly what you have in mind here but I want to clarify that export const {method} = C.prototype is not enough to get tree shaking. Such a method is still attached to the class and therefore still subject to polymorphic/reflective use cases which are what make tree shaking unsafe. |
| 21:07 | <Evan Winslow> | Also the data flow aspect of call-this comes from being chainable, which is true of syntax but not of your callThis helper. To support arbitrary functions with call this, it doesn't take long to come up with a helper like function to(fn) { return fn(this); }. Which then let's you do obj..to(Object.entries).filter(fn1).map(fn2)..to(Object.fromEntries); |
| 21:29 | <Richard Gibson> |
Even if |
| 21:41 | <Richard Gibson> |
this is definitely true, but seems irrelevant. It's also already possible to get poor-mans-pipelines using helpers with names like |
| 21:56 | <Evan Winslow> | My point was that the spelling of the operator (appears to me) is a non-factor here. So I moved on to secondary considerations. |
| 22:01 | <Richard Gibson> | I think ljharb is saying that the spelling is a factor—and if he's not saying that, I'm certainly willing to, specifically because a misspelling of .. as . is still valid syntax with plausibly undetected behavioral differences, as opposed to alternatives. But regardless, moving on to other considerations seems fine. |
| 22:04 | <Evan Winslow> | How does one avoid importing C? At some point your program has to instantiate the thing in order to call any methods on it, right? |