tc39 / proposal-pipeline-operator

A proposal for adding a useful pipe operator to JavaScript.
http://tc39.github.io/proposal-pipeline-operator/
BSD 3-Clause "New" or "Revised" License
7.55k stars 108 forks source link

I think it would be prudent to walk away from this proposal entirely #297

Closed benlesh closed 1 year ago

benlesh commented 1 year ago

Looking at #91, and remembering several other threads. There is just too much disagreement. Anecdotally, I went from being beyond excited about the possibility of adding this feature, to now begging the committee to stop it and do nothing instead, in the hopes that someday JavaScript developers will have something better.

A 2018 tweet showing my genuine excitement about this proposal.

Given standardizing the current proposal would eliminate the chance for a pipeline operator that behaves in a way that's more useful to existing functional pipe using libraries and users (which generally leverage currying or unary functions). And given the fact there has been years of strong, valid resistance to the Hack style operator proposal. IMO, it would be best to do nothing instead. It's far better to keep the status quo than to add something questionable.

The objections to the Hack-style proposal are well documented in this repository. Like it or not, this proposal is an example of why some folks feel the committee imposes their will on the community rather than being a part of it. In fact, I don't really expect this issue to be taken seriously. I'd wager by the time some folks have read to this sentence, there are emotions filling their hearts if they disagree with what I'm saying. I just think it's a mistake to proceed... and better to wait until we have a unanimous agreement. Which may never come, and I'm okay with that.

benlesh commented 1 year ago

Complete respect to @tabatkins and folks working on the proposal. They've fought a lot and done a lot of VERY hard work. I'm just humbly begging we put it on ice. I'm unconvinced the current incarnation is a good idea.

VitorLuizC commented 1 year ago

this proposal is an example of why some folks feel the committee imposes their will on the community rather than being a part of it

I would add this comment on Promises spec, where we can see clearly how committee acts against functional programming paradigm in JavaScript.

https://github.com/promises-aplus/promises-spec/issues/94#issuecomment-16176966

edit

I also like Hack pipelines, and i really think its way better than F# because it allows the usage of class methods and other operations that aren't unary functions. It would bring imperative/OOP to funcional way of expressing things.

Hack is waaaaaay less cursed than F#.

const createUsersCache = (users: User[]): Map<number, User> =>
  users
    |> #.filter(Boolean)
    |> #.map((user) => [user.id, user] as const)
    |> new Map(#)
const createUsersCache = (users: User[]): Map<number, User> =>
  users
    |> ((users: User[]) => users.filter(Boolean))
    |> ((users: User[]) => users.map((user) => [user.id, user] as const))
    |> ((tuples: readonly [number, User]) => new Map(tuples))
benlesh commented 1 year ago

The above response is fairly off-topic. Whether or not the committee "acts against functional programming" isn't the issue. The issue is this proposal is controversial, and IMO, it's better to add nothing at all.

The examples were odd too. With intentional verbosity removed:

const createUsersCache = (users: User[]): Map<number, User> =>
  users
    |> (users) => users.filter(Boolean)
    |> (users) => users.map((user) => [user.id, user] as const))
    |> (tuples) => new Map(tuples)

But also, all examples above should be rejected at code review, because it's a silly way to use a pipe:

const createUsersCache = (users: User[]) =>
  new Map(users.filter(Boolean).map(user => [user.id, user]))
Avaq commented 1 year ago

I agree with this sentiment. Given that the Hack pipeline operator has the potential to cause a divide in the (already small) functional programming community*, and reduces the chances that we'll ever get better HoFP support, I'd rather not see it make it into the language at all.

* As I've tried to demonstrate, [adopting data-first call style for the Hack operator] would be a move away from the fundamental paradigm of higher order functional programming. A move that I can say with confidence many of the members of the community I serve would not want to make. Some within the community who feel less strongly about the benefits of HOFP might adapt, yes. But that's exactly what I mean when I say marginalization: while the community at large might converge, the HOFP community will experience a divide. -- https://github.com/tc39/proposal-pipeline-operator/issues/233#issuecomment-932737570

noppa commented 1 year ago

valid resistance to the Hack style operator proposal

Most resistance I've seen boils down to either "This should just be a function, not a new operator" or "F# version is better".

I think the former argument has been pretty well refuted in issues like #293. The latter argument about F# variant is more persistent, but kind of moot. F# has been tried but didn't pass, and all evidence suggests that it never will. Argumenting for F# over Hack is a dead-end.

Are there issues that show how Hack leads to a worse situation than where we are now, i.e. no built-in "blessed" way to pipeline function calls or other expressions at all?

I liked F# (especially with the separate partial application proposal) more too. But now I just hope that we get something. I encounter a situation where this would make my code cleaner like once a week at least (although admittedly about half of those would also need #198). Sure I could import some "pipe" helper function and write a bunch of piped lambdas. But I won't. Or I could use one useless helper variable instead of several "const steps" and make that a "poor man's pipe operator". But I won't. It's just not how I've gotten used to writing JavaScript and it's not how my teammates have gotten used to seeing JavaScript, and it's just not worth it if I have to worry about questions like "will using these helper functions make my code slower" or "does this make things harder to read or debug" or "how many steps should a pipeline have before it's worth it to import and use that function". Simpler to just write the damned assignments to intermediate consts. Ease of use and familiarity makes or breaks a feature like this, which is why I would very much love to have it baked into the language, in one form or another.

Taking this as an example

const createUsersCache = (users: User[]) =>
  new Map(users.filter(Boolean).map(user => [user.id, user]))

I don't think I'd ever write

import pipe from 'mypipelib'

const createUsersCache = (users: User[]) =>
  pipe(
    users.filter(Boolean).map(user => [user.id, user]),
    _ => new Map(_)
  )

but I think I do actually like this version very much

const createUsersCache = (users: User[]) =>
  users
    .filter(Boolean)
    .map(user => [user.id, user])
    |> new Map(%)

:shrug: IMO that's easier to both read and write from left to right, without having to go back to wrap the whole thing in new Map once you realize that's what you have to do. And given that there's zero runtime overhead for doing this, I don't think it would be silly at all.

benlesh commented 1 year ago

(already small) functional programming community

Well, sort of.

RxJS on npm with 47M downloads a week

With this one library alone, it would cause a ton of churn to move everything over to be more useful with the Hack-style pipelines. And this is definitely not the only library making use of this style of composition.


Unrelated... Can we stop with the silly array to map example? It really doesn't make sense, and it's completely off-topic from what I'm suggesting.

VitorLuizC commented 1 year ago

it's better to add nothing at all.

Based on what? The small inconvenience with today's pipeable libraries?

So, instead of adding a feature that would work with all paradigms and libraries we should cancel everything and wait for something to happen in the future that will only help writing code with libraries like rx, fp-ts etc?

benlesh commented 1 year ago

I will assume that is a rational question asked in good faith. Based off of how contentious this discussion has been. I disagree that this works well with all paradigms. But that's beside the point. The point is the community is clearly divided on this, with the exception of a powerful minority. And that's no way to run things.

lucacavallaro commented 1 year ago

currying and unary functions are only used in 3rd party libraries, most of the language's existing features would not benefit from a pipeline in that way

I really struggle to understand the point of those who prefer the F# style

lozandier commented 1 year ago

@lucacavallaro …I would strongly refrain from the absolute language stating what is essentially the most common and simplest way to use functions with input & output that can be used by another function (or final result) is “only” used in 3rd party libraries…

The most essential means of composing, chaining, and communicating working with functions is functions with the arity of one (unary functions).

The fact that hack-style makes this essential means of working with functions the most tedious to write is one of the primary reasons it is so contentious with an obviously very powerful minority essentially hand-waving that’s okay.

It’s been clearly communicated in #217 that it’s not to very meaningful segments of the JS community. It accordingly seemingly doesn’t seem it’s in the best interest for this proposal to be continued as is considering the seemingly far smaller ecosystem-impacting reasons standards pivoted from a certain direction (IIRC smoosh-gate).

It can be argued that Hack-style is continuing exploiting the unceremonious means it’s been pushed by a powerful minority with JS members giving in only because it seems it’s it OR nothing at all. That’s almost as contentious as you can get without abandoning a standard.

As far as I’m aware, it’s unprecedented the most essential and most purest way of doing a computation is less tedious than using its operator in JS—can you point us to an example in the language another operator does this to what it’s supposed to make more easier and clearer to write?

Finally, a great deal of the language has methods that aren’t and weren’t intended to be composed in a data pipelining manner that is disingenuous to bring up as they’re often as they are in these matters. Such methods are usually rewritten with fascade variants oriented/optimized for chaining to be less tedious to write in everyday codebases.

tabatkins commented 1 year ago

Looking at https://github.com/tc39/proposal-pipeline-operator/issues/91, and remembering several other threads. There is just too much disagreement.

There is no more disagreeement now than there ever was. There continues to be a population of authors who use a lot of higher-order functions and want a pipe that caters to them, to replace the pipe() function they're currently using. The "F#-style" pipeline was explicitly rejected by the committee. The committee's reasoning is documented in this repository, and none of the champions see a way around those objections, nor do they particularly want to pursue that direction in any case. Anyone wanting to pursue a proposal in that direction is welcome to, but they are very likely to fail for the exact same reasons it failed in the past.

This proposal will continue in the current direction. The champions have been working on other things so work has been paused, but not stopped.


As has been stated before, a proliferation of topics re-litigating the exact same issues doesn't help anyone. We have a few topics covering the various angles of this; please use those for discussion if there is new information to be had. Do not revive the threads if there is no new information, however; review the existing posts first and, if the points you wanted were already made, leave it be.

I'll be closing this issue.

benlesh commented 1 year ago

This isn’t relitigating any issue. This is pointing out that there’s a big disagreement in the community, and that the best course of action is probably to do nothing.

This is dismissive. I suppose I have to pay for entry.