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.5k stars 108 forks source link

Viability of using |: as the operator for Hack-style #237

Closed shuckster closed 2 years ago

shuckster commented 2 years ago

Pulling this from the readability thread. I hope this is different enough from both that and the Tacit unary function application section of the main proposal to warrant a separate discussion.

|: has been suggested as an alternative operator to |>

value 
  |: extractBusinessData(^) 
  |: await mergeWithWebServiceData(^) 
  |: processResults(^) 
  |: printResult(^);

This would avoid foreclosing on using |> for a potential future operator familial to FP'ers.

Quoting from @lozandier's essay:

While I'm adamant a tacit, first-class functional-composition pipeline operator should ship in the language as originally desired by this proposal, I'm even more adamant hack-style doesn't prematurely reserve the ubiquitous means of representing such operators for its usage.

--@lozandier

It seems fair to note that @mAAdhaTTah originally proposed |: for Split Mix. I don't want to speak for him other than to honour the originator of the idea, but I feel it's important to bring in this quote:

I just want to clarify that I was not suggesting I was open to Hack-style being |:

--@mAAdhaTTah

I hope it's okay to suggest it separately here.

EDIT - Correction from @mAAdhaTTah , it was originally suggested here: https://github.com/tc39/proposal-pipeline-operator/issues/75#issuecomment-362380660

A few posts have in the readability thread popped up in support of |: (along with their respective emojis) so I thought we might be better off with a dedicated thread to expand on these thoughts and hear about the viability of this idea from the champions.

sandren commented 2 years ago

Adopting |: as the syntax for the proposed Hack-style operator leaves the language open to the possibility of introducing a proper |> operator in the future.

Doing so would alleviate at least some of the concern of those in the FP JS/TS community who are disappointed that the minimal or F# proposal did not reach Stage 2 and are afraid of being locked into pipe() forever.

However that would mean starting over with a new |> pipeline operator proposal at Stage 1. Is there someone who would champion it?

mAAdhaTTah commented 2 years ago

To be clear, I'm not the originator of the idea but linked back to this comment where it was originally suggested as the placeholder version for Split Mix.

tabatkins commented 2 years ago

The only reason for changing to |: would be to avoid taking |>, right? To the best of my knowledge, there's no suggestion that it's a better operator in some way?

shuckster commented 2 years ago

That seems to be the reason it resurfaced as a suggestion, yes.

Mind you, I'd welcome seeing fewer > characters amongst all that JSX. 🤔

mmkal commented 2 years ago

Another advantage is that it looks like a slightly judgemental 😐 face so could subtly shame people into not writing bad code.

aadamsx commented 2 years ago

I like the idea of |: operator as the "pipe" and also adding : as a matching placeholder like so:

// Hack with alternative pipe operator and placeholder |: & :

value 
  |: extractBusinessData(:)
  |: await mergeWithWebServiceData(:)
  |: processResults(:, 1, 2)
  |: printResult(:);

Makes it clear that the placeholder is associated with the pipe in a way.

lightmare commented 2 years ago

I like the idea of |: operator as the "pipe" and also adding : as a matching placeholder

You cannot have both, because it would lead to ambiguity:

one |: foo(:) |: +bar(:) // ambiguous
+bar(foo(one)) // is it this?
foo(one) | one + bar(one) // or this?
aadamsx commented 2 years ago

You cannot have both, because it would lead to ambiguity @lightmare

Will you please be more specific, for example:

// How can this:
one |: foo(:) |: +bar(:);

// Not be interpreated like this:
one |: foo(^) |: +bar(^): 

Thanks for your time.

mAAdhaTTah commented 2 years ago

@aadamsx Because | is bitwise OR, so it can be interpreted as:

one |: foo(^) | ^ +bar(^): 

The |: can be interpreted as either the pipe operator or a bitwise OR with the LHS value.

tabatkins commented 2 years ago

Yeah, : as a placeholder is really problematic on its own (I think it could produce ambiguous parses with the ?: trinary); combined with a |: pipe operator it just gets completely ambiguous from the get-go. Unary-plus is a source of so much fun potential ambiguity. ^_^

lightmare commented 2 years ago

Unary-plus is a source of so much fun potential ambiguity. ^_^

In this case unary plus is not the culprit, though ;) I used it because it helps to show where something goes wrong. It seems easier to spot than ambiguities caused by [] or conditionals. Such as one |: two[:] |: [:] or one |: foo(:) |: (: ? : : :).

I would add that even without those, : is kinda cursed, because it would also eliminate syntax options for potential future proposals like bind operator or array slicing.

tabatkins commented 2 years ago

Yup, let's just put the : placeholder to bed; it's definitely too problematic to consider both in current JS and for future JS proposals. If anyone wants to continue arguing for it, you can do so in #91, which is meant for discussing the topic token.

shuckster commented 2 years ago

While this thread mentions |: specifically, I have noticed the bikeshed tag (thank you) and also the HISTORY updates that point to the following comment from a proposal champion: https://github.com/tc39/proposal-pipeline-operator/issues/91#issuecomment-917645179

Pull-quote:

I've even mentioned up-thread that I'd support a Hack-style "topic pipe" like ^> or ||> or -> along side a F#-style function pipe like |>. That's a separate issue, however, and maybe we can discuss that outside of this issue as something to bring before committee. --@rbuckton

I hope it's okay to place here as a cross-reference.

lozandier commented 2 years ago

@tabatkins To explicitly confirm: Yes, Hack-style pipeline spec should avoid |> to enable its usage down the road for tacit, first-class functional-composition whether or not Function.pipe gets ratified.

tabatkins commented 2 years ago

(For clarity - this was a response to my question about whether |: was being suggested not because it was better per se, but because it avoided taking |>.)

I continue to be very much unconvinced by this, for several reasons.

First and most importantly, it is extremely unlikely that the committee will ever approve of a second pipeline syntax regardless of what pipeline syntax gets ratified, so pushing the current proposal to a less-good syntax to accommodate the barest chance of |> being used for something else isn't good stewardship of the proposal.

Second, "tacit" pipelines (aka F#-style), as has been established and documented in this repo, are far from the only pipeline semantic, and usage of |> is common for all the semantics. F#-style doesn't have "claim" over this syntax. If we do ever add an F#-style pipeline, it's just as reasonable for it to be spelled |: as it is for this pipeline to be spelled that way.

lozandier commented 2 years ago

Hey @tabatkins, I’m finally available again to have my attention briefly on this again now; sorry for the wait:

In the best interest of the hack-style proposal having the least path of resistance towards being a standard, I’m not sure what is there to gain with the proposal continuing to insist on the association of the |> tokens with the hack-style vs. other styles of pipelining. This has been tightly contested in this repo for years.

The pipeline operator advocates may want to consider more holistic equitable solutions that can enable the co-existence of the hack-style form of pipelining with other forms of pipelining. I think a good gesture towards that is the hack-style disassociating itself from the |> token altogether that seems to be of little cost of the style being ratified.

Considering there even needs to be a Function.pipe() function rapidly suddenly picking up steam simultaneously with this proposal’s current plan to favor the hack-style , I’m not sure what is is there to lose in the progress of this proposal being ratified if the hack-style has its association with the |> tokens removed so those tokens can instead be reserved for a more functional-composition-oriented form of pipelining in the future (or even something else like a 1:1 shorthand for Function.pipe).

The simplest solution to that problem to me is disassociating the |> tokens from the hack-style form of pipelining; doing this enables hack-style to be a less obtrusive addition to the language; such an decision would avoid entirely the narrative and ongoing controversy that hack-style is attempting to override how many think of pipelining with |> by a meaningful amount of the JS community.

While I too believe that the F#-style has little chance of passing today and in the near future by the current incumbents of the TC-39 committee, it shouldn’t mean we have to severely cut off such a popularly desired outcome in the language anytime soon to the extent associating |> to hack-style does.

My Rationale More In-depth

I cannot emphasize enough the importance of the pipeline operator advocates needing to consider that they may need to be more mindful that a meaningful amount of the existing JS developers that originally supported, have prominently used, and have actively advocated for a pipeline operator in JS towards its continued relevancy in standardization processes want a version of pipelining that tacitly communicates functional composition primarily that the hack-style is explicitly unorthodox to; accordingly I believe the hack-style explicitly disassociating itself from |> needs to be seriously considered.

Similar to the what lead the proposal of Array.prototype.flatten existing instead needing to be Array.prototype.flat because a tool with a meaningful amount of use on the Web still already leveraged Array.prototype.flatten for its functionality, a meaningful amount of the existing JS community use meaningful libraries, created meaningful and delightful edu resources, and cultivated communities with meaningful prominence in the JS community within and independently from frameworks recognize pipelining with the semantics more akin to the F#-style. Correspondingly, the continued association of |> with #F-style pipeline behavior wouldn't ‘break the Web' / Dev ecosystems, but hack-style outright does.

Accordingly, the Pipeline operator advocates shouldn’t be surprised that members of the JS community that are in favor of the F#-style still are attempting to have meaningful influence on the progress of this proposal with the latent function of hoping the pipeline operator advocates have them more in mind indefinitely.

Disassociating the hack-style with the |> tokens should be thought of a low friction way of significantly improving the progress of this proposal by navigating around the meaningful amount of pushback it has had the past few years more independently from such a demographic–even if the current group of pipeline operator advocates see such an audience too much of a minority to accommodate to the dismay of those audiences. I again tried to be helpful explaining why that is in a comment months ago.

The hack-style disassociating itself with |> would maybe make existing preprocessor-plugin authors (such as the ones associated with the babel community) jobs much easier as well.

Accounting for Newcomers

In addition to existing JS community members, I’m aware the pipeline operator advocates have to account for newcomers too… Nonetheless, a meaningful amount of people in that audience–especially in industries/domains such as data science–are also familiar with functional-composition-oriented pipelining the hack-style is again unorthodox to. I’ve pointed this out in this Github repo a few months ago rather favorably.

Regarding the current TC-39 committee

All that said, I’m well aware in addition to these audiences, The Pipeline advocate ultimately answer to the TC-39 committee. I hope current members of the TC-39 committee can be more vocal if they’re even aware of these nuances prior to their current stances on the matter. I think they’ve put an unfair amount of burden on the previous and current pipeline operator advocates–as well as a meaningful amount of developers surveying this survey waiting for any form of data-pipelining-oriented affordances being standardized in the language soon at this point.

Conclusion

Overall, in the specific pursuit of the hack-style being in the best position of becoming a reality, I’m not sure how the cons outweighs the pros of the hack-style formally disassociating itself from the |> tokens. Committing to the dissociation of hack-style with the |> tokens would again also free up an opportunity for the behavior of the |> tokens instead be associated with Function.pipe that may be ratified–or another pipeline operator standard more similar to F#-style one day–or emulated by custom preprocessor and parsers to enable the existence of the hack-style be less obtrusive to a meaningful amount of the JS community.

Especially when it can be argued that the F#-style is more inclusive and efficient to more forms of programming styles and JS audiences than hack-style’s current behavior and semantics ; the F#-style is a more tacit means of representing functional composition and class mixins, as well as consistently requiring less characters needed by a JS developer’s input device of choice to execute pipelining behavior meaningfully conventional inside and outside the JS community.

I totally understand the current pipeline operator advocates have a very unique challenge getting this proposal to a point it can be a reality; having to weight / consider all these things is something I don’t envy them doing!

However, changes like this that I find to be a low-friction change should probably be seriously considered more frequently for proposals like this to progress easier towards being ratified that are more mindful of the past present, and future of pipelining in JS (as well as other features a meaningful amount of the JS community want that are historically tied to functional programming and other meaningful styles of programming today not yet well represented in the language) .

I think such approach being more commonly considered with standards like this would be more accommodating of all existing and potential programmers wanting to program things in a pipeline-oriented way on the Web platform using JS.

//cc @js-choi @mAAdhaTTah @rbuckton @littledan

snatvb commented 2 years ago

is this pockerface? 😐

I think that |>, <|, -> more common and understandable.

It will be much easier to explain to people how it works that had no experience with pipe

lozandier commented 2 years ago

is this pockerface? 😐

I think that |>, <|, -> more common and understandable.

It will be much easier to explain to people how it works that had no experience with pipe

This is a tangent/strawman to the point I'm making; I made no objection/mention of <| nor -> being used by hack-style or any other style of pipelining instead.

As far as |>: One of my main points is that a meaningful amount of existing members of the JS community–especially notable libraries, APIs, and resources from them–associate |> with a style of pipelining that hack-style is unorthodox to.

tabatkins commented 2 years ago

I’m not sure what is there to gain with the proposal continuing to insist on the association of the |> tokens with the hack-style vs. other styles of pipelining. This has been tightly contested in this repo for years.

I don't think this is accurate. What was contested was which pipeline behavior we should use in the first place. The README documents that |> is used across languages for all the variants; tacit-style has never had a strong claim of being the "common" version. (The major variants being topic-style, tacit-style (which is, in most languages with it, also last-arg-style, due to them commonly having auto-currying behavior), and first-arg-style.)

so those tokens can instead be reserved for a more functional-composition-oriented form of pipelining in the future

There is an approximately 0% chance of the committee approving two close pipeline variants, now or in the future. "Reserving" the |> glyph would just, in practice, be retiring it.

Similar to the what lead the proposal of Array.prototype.flatten existing instead needing to be Array.prototype.flat because a tool with a meaningful amount of use on the Web still already leveraged Array.prototype.flatten for its functionality,

Our situation is not meaningfully comparable to the flat/flatten situation. There, a library in active use was squatting on the exact name, with slightly different semantics, in a way that would break sites when browsers introduced the standard version of the function. Our situation is a new operator which by definition cannot be used today; it can potentially be used in JS-derived languages compiled on the server, but those don't get seen by the browser either, so there's zero chance of breakage.

(Such langs do theoretically offer future pain for users of the tool who try to use the JS-standard version of the operator and instead get the lang-specific one, but that's a permanent risk for such JS-derived languages. Whether or not it's significant enough to be worked around is a case-by-case assessment; see similar situations in CSS and Sass where we've gone both directions.)

a meaningful amount of the existing JS community use meaningful libraries, created meaningful and delightful edu resources, and cultivated communities with meaningful prominence in the JS community within and independently from frameworks recognize pipelining with the semantics more akin to the F#-style.

Yes, and all of these would be well-served by Function.pipe() as a direct drop-in replacement. None of them use |> or any comparable operator today, so whatever behavior we use, and however we spell it, won't affect any existing code, just inform how future code might get written. (And as has been argued many times in the past, and as currently documented in the README, pipeline works just fine with such code as it stands today.)

(I'm going to skip past a large amount of text implicitly relitigating the pipeline behavior; that's settled and isn't changing. This topic is solely about |:.)


In conclusion, the argument here is that |> is strongly associated with a particular pipelining behavior (tacit-style) among at least some authors, and as our pipeline proposal is using a different behavior, it should move to |:, so as to either leave open the possibility of |> as the tacit-style pipeline in the future, or at least prevent such authors from being confused when they read or write new JS code with pipeline in it.

I disagree with all of the premises here:

So, I continue to disagree that we should make any change here.

lozandier commented 2 years ago

I don't think this is accurate. What was contested was which pipeline behavior we should use in the first place. The README documents that |> is used across languages for all the variants; tacit-style has never had a strong claim of being the "common" version. (The major variants being topic-style, tacit-style (which is, in most languages with it, also last-arg-style, due to them commonly having auto-currying behavior), and first-arg-style.)

I'm not disagreeing on that; |> was consistently primarily used for all of the pipelines discussed so far with the assumption they were all competing to be the pipeline behavior associated with |> as the sole pipeline operator ratified by this proposal . Even when “split-style” solutions were considered, a meaningful amount of JS developers throughout many issues in this repo insist that pipelining akin to F#-style was associated with |>.

As far as tacit-style has never had a strong claim of being the "common" version: There is a sizable amount of existing JS communities and users (in the millions) that are accustomed to linearizing functions in the matter F#-style explicitly accommodates when it comes to a particular terse way to communicate chaining/composition.

The proponents of the hack-style on the other hand has not shown much quantifiable data that there has been interest of pipelining with functions not being the primary means to do so.

The hack-style is under the presumption being able to pipeline more things with its default semantics makes it better.

It can be argued that hack-style is a solution looking for a problem being a premature optimization for non-functions with its default semantics when demand for pipelining in such a way or understanding the idea easier with such semantics is maybe the smallest minority amongst all relevant demographics considered for the proposal thus far.

There is an approximately 0% chance of the committee approving two close pipeline variants, now or in the future. “Reserving” the |> glyph would just, in practice, be retiring it.

I’m extremely confused why that is the case–which contradicts the README BTW–and why that can’t be changed in the future. The make-up of the committee seems inevitable to change over time as people resign, be swapped out for others, retire, and so on. Again, it would be useful for committee members to break their silence on such matters.

Anyhow, If it’s true there’s 0% chance of the committee approving two pipeline variants, I find that an unnecessary roadblock for this style of pipelining that has some good reasons to exist and be ratified; I however cannot imagine it being received well this pipeline behavior being approved at the expense of permanently blocking F#-style or the “implicit” pipelining behavior enabled by Function.pipe.

I would not be surprised Function.pipe() gets ratified before this one given the contentious of hack-style being the only pipeline behavior in JS would cause; I’m not convinced there won't be interest of simplifying the behavior of Function.pipe be more easily accessible to JS des eventually with less tokens that is related to why many are hoping the #F-style is ratified eventually someday regardless how many times it has been rejected so far: If at first you don’t succeed with a meaningful idea, you try again (who knows what happens).

I find the stance of not approving two pipeline variants by whoever is leading that stance is setting up any style of pipelining to not be part of the language indefinitely. Such a stance seems unnecessary, unnecessarily antagonistic of more holistic solutions becoming prominent over time, and not inclusive of the needs JS developers have communicated throughout this process towards this proposal being relevant enough to go through the standards process in the first place.

Maybe it would be a good result for neither hack-style and F#-style be standardized; perhaps that outcome would communicate this proposal was too big/ambitious all along with the way the standard process is handled today and perhaps the rest of our lifetimes.

Perhaps more focused specs such as Function.pipe and the [partial application spec] being ratified first is the right way to go associated with the problems this proposal originally hoped to solve independently.

Retiring |> seems to be a minor inconvenience in comparison.

Our situation is not meaningfully comparable to the flat/flatten situation. There, a library in active use was squatting on the exact name, with slightly different semantics, in a way that would break sites when browsers introduced the standard version of the function

I’ll clarify where I think the similarities lie: The world of JS today transcends just browsers; RxJS and other popular libraries in Node and so on have embraced the F#-style is in the millions. That’s a meaningful amount of users and libraries that hack-style being associated with |> would disrupt that I believe should be more thought about with decisions related to keeping hack-style associated with |>.

There's again popular things that have taught and expected a style of pipelining hack-style is explicitly unorthodox to causing what I find unnecessary cognitive noise and an unnecessary amount of work to put on members of the JS community.

It’s not congruent to the flat situation since that situation was jeopardizing sites and users, but it’s meaningfully comparable with the amount of people and popular JS libraries hack-style being assigned to |> disrupts.

I hope this makes it more clear why I made that comparison.

Yes, and all of these would be well-served by Function.pipe() as a direct drop-in replacement. None of them use |> or any comparable operator today, so whatever behavior we use, and however we spell it, won’t affect any existing code, just inform how future code might get written. (And as has been argued many times in the past, and as currently documented in the README, pipeline works just fine with such code as it stands today.)

I think you’re tremendously minimizing what has appealed to the communities behind such things wanted in this proposal they hope the broader JS community can have everyday access to: A terser way to communicate an input transformed over one or more tasks/processes (functions) to produce a particular result (a single result or multiple in the future as a tuple) using the same or less characters than chaining; Bare expressions being implicit functions with the first parameter being the input of the last process. Note pipelining is alternately called linearization in non-JS and non-programming communities I’m part of.

A meaningful amount of people want process chaining by default to require less characters required to communicate their programming intent: That is effectively tacit, first-class functional composition.

Hack-style again is explicitly unorthodox to those concerns to instead have pipelining semantics with functions less of a default . This is to the annoyance of a meaningful amount of people that continues to stall this proposal and often leading to obvious division between the champions getting people onboard towards a pipeline being ratified without issues like this needing to exist.

I’m not convinced that wouldn’t be the case if the greater JS community was surveyed on their preference; your related stances that “heavy use of point-free programming produces code that is difficult to read and understand” seems to be contradictory to the popularity of a variety of things used by the millions within the JS community today.

Accordingly, I strongly recommend the champions and/or the committee to actively get quantitative data on the matter to support various implicit and explicit decisions associated with this proposal considering how transformative its impact will be + how disproportionally contentious it has been getting it to progress.

As of right now, I’m not convinced you and the other pipeline champions with similar views could adequately defend against claims you’re committing the appeal to minority fallacy that can’t be properly challenged by the JS community and other impacted people/entities because the way the standard process currently works.

Nonetheless, it’s abundantly clear to me it’s not “fine” to a meaningful amount of people that basic functional chaining attempting to be more clear communicating its outcome requires (^) in addition to |> that accordingly makes pipelining functions requires more characters to communicate than chaining regularly for a variety of functions like unary ones.

I would argue this is an a11y issue; a case be made that pipelining functions shouldn’t require more characters than imperatively method chaining–unary functions or variadic functions.

A code base transformed to dominantly use hack-style pipelining will require more input/chars needed to communicate such ideas than F#-style and existing userland code leading to more effort and ultimately bigger file sizes.

It is not by accident that f(g(x)) can be communicated as x |> g |> f with the same amount of non-space characters that explains the linearization in a clearer way to a meaningful amount of people that have historically wanted the pipeline operator to exist. Hack-style does not accommodate this at all.

Because of this, similar to what I perceive to be the intent of #238, I recommend removing method chaining as a primary use case for hack-style pipelining. Function.pipe and hopefully something like F#-style in the future would be far more appropriate

Function.pipe helps vanilla JS user have this behavior be partially available out-of-the-box that certainly helps environments where libraries are actively discouraged or impossible (unblocking strategies that wanted to communicate problems this way) but it hardly benefits existing JS users who pipeline using popular libraries such as Ramda and RxJS already.

They–along with the peers in the machine-learning communities I’m more part of–want to be able to communicate consumable data processed left-to-right sequently by functions with no tokens such as ((^)) or Function.pipe needed to represent first-class composition with the same amount of characters or less than composing the functions normally.

I nonetheless intend to address this further in issue #215 when I next get the chance being outside the scope of this issue.

  • The community of authors that also program in strongly-functional languages that have a tacit-style |> operator, and thus might be confused by |> in JS acting slightly differently, is very small compared to the population that has never seen |> at all and will just learn the JS behavior as natural. As well, such languages have many shared syntaxes with JS that also work slightly differently, and those don’t cause major confusion today, so it’s not clear why |> in particular would be problematic.

I am not convicted that the the general population would see hack-style as natural whatsoever.

Engaging these discussions more of a representative of the data science and machine learning communities than the JS functional programming communities, I find the hack-style unnecessary at odds with the use of unary functions to conceptually and programmatically model pipelining/linearization.

Unary are prominently the basic unit of work in today’s most prominent and rapidly computing sectors such as the ones associated with cloud computing and data science. As demonstrated briefly in response I posted months ago, popular languages and tools by such communities support this such as Julia, reactive-extensions, data/machine-learning pipelining abstractions offered by cloud providers; and so on.

Anecdotally, I’ve yet to see peers that have to use JS—or have interest in using JS to modeling their problems in a pipeline-oriented manner—be warm to hack-style as-is because it is combative of the simplest form of communicating pipelining behavior to them: unary functions.

Accordingly, that is why I think hack-style being the only pipeline operator in JS would be a big mistake as the only pipelining behavior in JS without affordances added to it to better accommodate unary functions at least. If it’s hack-style or no other forms of pipelining being present in the language, I’m warming up to the idea of the latter. I would find that outcome with Function.pipe in existence along with other specs such an eventual partial application spec far more natural than hack-style becoming the sole form of pipelining in the language with the way it handles unary functions as of today.

Again I’ll look into discussing further in #215 or another issue in this repo to discuss this in greater detail.

  • |: does not have any pedigree among existing languages at all, so authors wouldn’t be able to lean on any intuition about it.

That’s seemingly intentional by the original person that filed this issue as a beneficial thing to minimize cognitive noise associated with the eventual JS behavior of how pipelining/linearization works

The use/semantics of |> by a meaningful amount of people in and outside of the JS community is of pipeline/linearization behavior that hack-style is unorthodox to.

With the use of something like |:, Authors will not be able to lean on any intuition about the behavior of pipelining that has already been circulated by meaningful JS communities how |> ought to work in their eyes and so on to just focus on learning the behavior of hack-style being what it ultimately becomes throughout the remainder of the standardization process.

Even if hack-style becomes ratified as the only pipeline operator, there would be a lot of superfluous work/time/effort by such communities and the champions/committee to help people confused by the material out there using |> in tacit ways that hack-style is again fundamentally unorthodox to. Such issues become a non-issue if hack-style disassociates itself from the |> tokens.

tabatkins commented 2 years ago

I'm going to continue to not address any of the more generic arguments for or against tacit or topic style, as those have been addressed elsewhere and are not the topic of this issue.

I however cannot imagine it being received well this pipeline behavior being approved at the expense of permanently blocking F#-style or the “implicit” pipelining behavior enabled by Function.pipe.

That exact thing was indeed received well by the committee. The pipeline proposal stalled several years ago because several committee members disliked tacit-style for several reasons. So it's already a reasonably well-established reality that a tacit-style pipeline operator isn't going to make it into the language at all, let alone alongside another pipeline operator with nearly identical semantics.

The make-up of the committee seems inevitable to change over time as people resign, be swapped out for others, retire, and so on.

Sure. That doesn't mean we leave every issue open forever just in case the membership thinks differently in a few years. The committee has weakly leaned in a particular direction for several years now, and the champions here lean more strongly. If that changes, it changes, but we're not exactly balanced on a razor's edge here.

Again, it would be useful for committee members to break their silence on such matters.

There's no need to imply a silent majority here. The minutes of our meetings are public, you can see what people said in the discussions.

Such a stance [about TC39 only ratifying a single pipeline syntax] seems unnecessary[...]

You don't have to believe me on this regard, as whether or not it's true isn't actually that important. The fact remains that topic-style is the pipeline variant currently being championed, and |> is the glyph we'd like to use for it (since it's the common glyph across most pipeline variants), and no other pipeline variant has a sufficiently strong "claim" on the glyph to justify us moving off of that syntax. (On the other hand, note how much wiggle-room we've had for the topic syntax, as we want to avoid stomping on other likely syntaxes like records/tuples. Being polite about syntax space is a good thing when there's a good argument for it!)

RxJS and other popular libraries in Node and so on have embraced the F#-style is in the millions

No, RxJS uses a .pipe() method that accepts functions. It does not use tacit-style pipeline syntax, which is what I was arguing.

(Below I get into the more meta question of why even this is still compatible.)

I think you’re tremendously minimizing what has appealed to the communities behind such things wanted in this proposal they hope the broader JS community can have everyday access to: A terser way to communicate an input transformed over one or more tasks/processes (functions) to produce a particular result (a single result or multiple in the future as a tuple) using the same or less characters than chaining; Bare expressions being implicit functions with the first parameter being the input of the last process. Note pipelining is alternately called linearization in non-JS and non-programming communities I’m part of.

No, I'm not minimizing anything here. Linearization is indeed the entire point of pipelines, but every syntax achieves it, just with varying degrees of convenience for some kinds of operations.

As the README says, literally the only difference in tacit-style vs topic-style is whether it's slightly more convenient to use with unary functions (tacit-style) or slightly more convenient to use with everything besides unary function (topic-style). That's it, and for either side, the "slightly less convenient" case is literally just three extra characters per pipeline step ((#) at the end of the expression for unary functions in topic-style, or x=> at the start of the expression for everything but unary functions in tacit-style).

(This is ignoring the await issue, which simply doesn't work in tacit-style and requires a special case in the syntax or explicit promise chaining from the author. It's also assuming that we bite the bullet on the syntax issues with an unparenthesized arrow function inside a pipeline step; if we can't resolve those, the tacit-style tax is five characters, on both sides of the expression - (x=>...).)

Trying to frame this as the champions rejecting an obvious and useful functionality in favor of a toy syntax they like better for some reason isn't productive. We can't have a meaningful discussion if you incorrectly believe that only tacit-style is capable of achieving your stated goals; that's simply incorrect and means we'll be talking past each other.

And now to blindly flail back in the direction of the thread's topic: that's why tacit-style doesn't have an automatic claim on the |> syntax. ^_^

Accordingly, I strongly recommend the champions and/or the committee to actively get quantitative data on the matter to support various implicit and explicit decisions associated with this proposal considering how transformative its impact will be + how disproportionally contentious it has been getting it to progress.

We have; the results are documented in the README. Short answer is the one user study that was done didn't produce a strong result either way, and the committee results from previous years rejected tacit-style.

We categorically reject the suggestion that we should bind ourselves by a novel and burdensome requirement to perform more (difficult! expensive!) user studies in order to advance. No other topic in this committee, or indeed in virtually any standards body I'm aware of, routinely sets such a requirement; nor was this requirement ever mentioned in the old days when this proposal was recommending tacit-style. We consider this to be special pleading.

I would argue this is an a11y issue; a case be made that pipelining functions shouldn’t require more characters than imperatively method chaining–unary functions or variadic functions.

You alluded to this argument in #238 as well; I invite you to answer the questions I posed there, as I don't believe this is a reasonable argument.

people confused by the material out there using |> in tacit ways

What material exists that does that, for JS? (If you're just referring to other languages that use the |> operator, I'll direct you back to the README's listing of the various pipeline styles that exist across languages; tacit-style is not the preeminent style, but rather one among several.)

lozandier commented 2 years ago

You've made it clear that tacit-style form of pipelining "doesn't have an automatic claim on the |> syntax" and so on regarding this issue. You've asserted this despite my argument that the spirit of tacit-style is very much more adopted by JS libraries (and their authors), most devs trialing the pipeline operator using Babel/TypeScript, and overwhelming more favorable support by existing popular JS resources prove otherwise. Accordingly, I'll refrain from replying additionally to this issue unless I'm adding something new on this topic beyond my replies below to some of the things you said or requested from me:

That exact thing was indeed received well by the committee.

I'm referring to reception by developers, including notable framework authors and seemingly the majority of developers tracking this proposal repo that I think most have made it quite clear they don't like this iteration of pipeline operator compared to the acknowledged superminority that is the committee who ultimately decides if this proposal goes one step closer to being ratified or not.

I hope this proposal doesn't get ratified without further affordances for bare chaining/composition calls at least.

There's no need to imply a silent majority here. The minutes of our meetings are public, you can see what people said in the discussions.

I'm aware of such public meetings; I'm talking about them explicitly reacting to the very apparent contentiousness of the direction of this standard between themselves, the current champions, and the JavaScript community.

I do not necessarily think there is "a silent majority". I've primarily encouraged the champions and committee to actively analyze how people think about the proposal outside this Github repository formally (user studies) and informally.

However, I will note that you seemingly have downplayed concerns about hack-style and defended its existing design decisions with majority/minority reasoning/logic more firmly than the logic I have elected to use to defend my arguments.

You seem to assume a silent majority finds topic-style more accommodating of pipelining needs today with the lack of quantitive and qualitative evidence besides making it clear you firmly believe that. I'm not used to the term "silent majority," so I apologize if I'm misconstruing what it means. I casually Googled what "silent majority" means before writing this.

No, I'm not minimizing anything here. Linearization is indeed the entire point of pipelines, but every syntax achieves it, just with varying degrees of convenience for some kinds of operations.

As the README says, literally the only difference in tacit-style vs topic-style is whether it's slightly more convenient to use with unary functions (tacit-style) or slightly more convenient to use with everything besides unary function (topic-style). That's it, and for either side, the "slightly less convenient" case is literally just three extra characters per pipeline step ((#) at the end of the expression for unary functions in topic-style, or x=> at the start of the expression for everything but unary functions in tacit-style).

With your last remark here, I then stand with my original statement you're downplaying the hassle of those three characters on unary functions to the meaningful amount of people who want this operator to more tacitly communicate functional composition than existing userland abstractions.

While true hack-style's topic-style form of pipelining makes the construct "slightly more convenient to use with everything besides unary function[s]," you're minimizing that the most elementary/common means of representing functions in a pipeline is unary functions. Instead, hack-style attempts to accommodate uncommon/foreign function composition use cases more than the use case. That's unintuitive to what an arithmetic operator is supposed to do with the expressions it's for. Functional composition is multiplication with functions, after all. This syntax inversion of making the typical case more verbose and tedious to write to accommodate far rarer cases is unprecedented in JS as far as I know.

Such token tax should accordingly be delegated to the more uncommon uses of composing / chaining (again, chaining is composition). I remain quite unconvinced the use cases hack-style optimizes necessitates them not getting the extra burden of needing additional tokens instead with the use of the theoretical operator.

You alluded to this argument in https://github.com/tc39/proposal-pipeline-operator/issues/238 as well; I invite you to answer https://github.com/tc39/proposal-pipeline-operator/issues/238#issuecomment-1159233591, as I don't believe this is a reasonable argument.

I've replied here. An abbreviated recap is the following:

Forcing (^) adds a lot more burden to a11y/power-user tools overnight (Braille/VIM/etc) for the simplest compositions (arguably greatly reduced as unary functions in this discussions thus far); it dramatically inconveniences such users and those who frequently chain/compose permanently rather than something more succinctly and more intuitively understood without such tokens they expect from a composition operator embracing an importantly desired trait of functional programming: tacitness.

Hack-style breaks the convention that a function won't use parenthesis unless outright invoked in a typical program. For them and many other demographics I find unlikely to like hack-style default semantics for functions, it's tedious and breaks conventions of higher-order computation modeling with functions. When functions are prepared elsewhere throughout the language to be executed later as-is or with normal multiplication, you don't need extra tokens (thus tacit) for the default use case; Hack-style deviates from this unnecessarily.

It's clear if a function is invoked in a pipeline with F#-style, it's producing a chainable result (another function or result), an anonymous function (inline processing of the previous result), or preparing a multi-variadic function to be invoked with the value of the previous result (when partial application spec is a thing).

When you're setting up functions to be conceptually computed implicitly throughout JS you don't use parentheses today; why hack-style deviate from this needing (^) with the simplest forms of expression composition (which is again multiplication with functions)? This is conceptually unneeded information in a functional composition operator when dealing with the most common/simplest representation of pipelining: With unary functions.

I consistently say the pipeline operator is a functional composition operator because that is what is ubiquitously understood of a pipeline being: a clear representation of function composition starting with the input. It's ubiquitous that people think of a pipeline as an initial input transformed by a series of processes/procesures to produce a particular value/outcome. In other words it is unidirectional functional multiplication.

Functions are the programmatic representation of such an abstraction towards modeling pipelining with syntax; functions encapsulate zero or more more expressions/statements that can later be invoked that a pipeline ubiquitously demonstrates how that's done with multiple functions.

Most functions in a pipeline mental model are unary because it's the simplest and easiest representation of a function that explicitly consumes the first or previous value of a pipeline before another function is called that latently assumes a function returns a single value. The same principle applies to multiplication.

Even if a function can return multiple values, it's intuitive that a function that returns two values would have those values be the first and second parameters of the following function to continue the composition (again functional multiplication). This is seamless to adopt in JS because most functions don't raise an exception when a function is invoked with more or fewer parameters than what the function was defined to accept. The edge case is if an exception is explicitly thrown after inferring such an invocation context using the arguments local variable inside non-arrow functions.

The computational flow of pipelining (composition) is also best equipped for unary functions in its most basic case and making (^) unnecessary.

// Regular composition; as explained in my formal response
// some try to chain with abstractions like `.pipe` at the cost
// of needing Endofunctors for the initial value or functions; and so on
// needed by things like  jQuery, RxJS, ramda, lodash, and etc. 
const output1 = function2(function1(input))

// Same expression alternatively represented
// as a data pipeline tacitly. 
// This alternate position of the functional identifiers
// and syntax explaining the eventual computional flow
// of the functions tends to makes the data pipeline more
// maintainable and clearer  to read with the same amount
// of non-space characters–especially for longer chains
const output2 = input |> function1 |> function2  

Even implicit/explicit currying–inlining the result from earlier in the pipelining chain to a function that accepts multiple parameters that you pre-fill some of the parameters of–accounts for the necessity that value(s) from earlier in the pipeline will be passed to the function–as you would expect from typical multiplication!

Accordingly, hack-style's forcing (^) with the most basic chaining expressions is unneeded information as demonstrated here:

// Hack-style: If `|>` clearly communicates pipelining and 
// the value of the function to the left has to be passed to 
// the following function–so why does the operator need `(^)`
// for the most basic composition/chaining expressions?
// It's more arduous than what it replaces which is 
// explicitly opposite of what people wanted from a 
// native functional composition operator. 
const output = input |> function1(^) |> function2(^)

JS innately makes it easy to express functional composition with functions tacitly because JS functions are first-class values (i.e. (func1, func2, func3), do not raise an exception if less or more arguments are passed into them (making it easy to understand how pipelining with tupling would work), have to return a value, and the existence of arguments allows you to always able to conceptually represent functions as function (arguments: any[]) {}: any.

JS being first-class is key since multiplication with primitive values does not need additional tokens to express how the previous value will be passed to the next–so why does function multiplication has to?

Such default token burden for such a common case seems conveniently done to make the pipeline operator less of a functional one independent of what most want to use the function for, what's best for the JS community, and what problems that people want to better communicate with JS when they have to write scripts with the language–a florian's compromise for the heck of it significantly reducing the impact of the concept in JS compared to the champions/committee better embracing its functional composition roots that has minimal blockers of being implemented in JS via F#-style.

Trying to frame this as the champions rejecting an obvious and useful functionality in favor of a toy syntax they like better for some reason isn't productive. We can't have a meaningful discussion if you incorrectly believe that only tacit-style is capable of achieving your stated goals; that's simply incorrect and means we'll be talking past each other.

Hack-style seems to be accommodating things that are more uncommon than typical chaining/composition and far less likely to be expressed in a pipeline than functions.

Accordingly, I entirely disagree that it can complete my stated goals throughout our exchanges. I'm curious what you think those goals are at this time; regardless, my core goals/expectations are the following:

Hack-style directly opposes this; nonetheless, I don't think we're necessarily talking past each other. I don't find hack-style to be a toy syntax. I do think it shouldn't be the default or only pipelining operator in the language because hack-style doesn't solve the biggest problems with functional composition expressions a functional composition operator usually solves that it isn't interested in solving according to you and champions of it so far (1): Tacitly communicating functional multiplication (composition/chaining) better than what devs can create.

one user study that was done didn't produce a strong result either way, and the committee results from previous years rejected tacit-style.

We categorically reject the suggestion that we should bind ourselves by a novel and burdensome requirement to perform more (difficult! expensive!) user studies in order to advance. No other topic in this committee, or indeed in virtually any standards body I'm aware of, routinely sets such a requirement; nor was this requirement ever mentioned in the old days when this proposal was recommending tacit-style. We consider this to be special pleading.

Regardless if it was in favor of any of our points in this discussion, I would say one study wouldn't be sufficient. I suppose if Function.pipe() was ratified first, it'd be an informal user study how well-received an exiting, mature means of attempting to functionally compose terser landing natively in the language is towards this proposal better accommodating that use case I think this proposal is strangely deviating from optimizing for despite it being the primary reason why people want a pipeline operator in the first place.

Regarding the precedence: I think the amount of contentious associated with the standard justifies the "special pleading". In your many years of handling standards as a champion, have you dealt with this much contentious between the JavaScript community and the champions/committee about the direction of a standard? I haven't.

If that's the case for you, it seemingly makes sense to go untraveled roads to get the spec less contentious than it is today.

I'm aware that's easier said than done; I don't see why sponsors/devs/champions can't discuss this further with all this in mind.

The pipeline operator is one of the most transformative new features proposed in JS–especially for those that want existing functional paradigm patterns, libraries, and code to be significantly easier to express. This includes devs like myself who deals with data pipelines often and have hoped for a long time for an easier way to write functional-composition-oriented in JS without the existing limitations of current abstractions.

Again @js-choi's Function.pipe() proposal to me is merely a more wordy but still appreciated lateral move of how the JS community has maturely attempted to tacitly compose in the language today without a native functional composition operator.

I think such unprecedented steps are justified considering how important and phenomenal the existence of such an operator would have. I'd love to finally use it outside of Babel and a particular TypeScript branch. I'd love my features alleviated that we will be long retired before a functional composition operator that makes chaining/composition easier exists.

Overall, Rose Dawson's GIF about waiting comes to mind frequently when I think about this proposal; I fear the proposal's progress will be indefinitely halted until the contentious about its verbosity with the simplest form of functional multiplication (unary functions) is alleviated: giphy

What material exists that does that, for JS?

Material such as the following I mentioned earlier in this issue.

Edit: Brainfart very late at night; I more meant to say functional composition is a binary operation for combining functions more than it being function multiplication. My main point still stands such operators don't need extra tokens for their base case; functions typically don't outside of JS either with the means composition operator are usually depicted in math such as the following:

Screen Shot 2022-07-07 at 1 11 46 PM

Hack-style breaks this convention established in JS for arthmetic/binary operators forcing the most basic form of functional composition (unary functions) to use (^). I'm unconvinced it's helpful or necessary for this to be the case.

mAAdhaTTah commented 2 years ago

You've asserted this despite my argument that the spirit of tacit-style is very much more adopted by JS libraries (and their authors), most devs trialing the pipeline operator using Babel/TypeScript, and overwhelming more favorable support by existing popular JS resources prove otherwise.

The mistake here is assuming that this is because a tacit-style pipeline is better for JavaScript or better overall and not because JavaScript's current syntactical affordances only enable tacit-style pipelines.

lozandier commented 2 years ago

The mistake here is assuming that this is because a tacit-style pipeline is better for JavaScript or better overall and not because JavaScript's current syntactical affordances only enable tacit-style pipelines.

Mistake? It's definitely not a mistake; I'm not being misguided nor necessarily wrong in arguing such assumptions are better to adopt than hack-style's latent premise that topic-style is better and its semantics would have been better received than tacit-style if it was equally feasible for developers to implement topic-style constructs in the language.

I'm not convinced JavaScript's current syntactical affordances only enable tacit-style pipelines is a factor here since the demand for more tacit means of programming in JS has lead to some of the most popular JS libraries, and in the past even CoffeeScript for the lack of improved tacitness around procedural and functional programming that ES2015 finally helped alleviate with things like arrow functions. No such movement have been done to prototype topic style of coding being more prevalent in JS (functionally or not) AFAIK.

Compared to that bold assumption, Topic-style pipelining seems more like a concerted effort to support linearization without enabling new levels of tacitness in the language more than it is alleviating a desire for pipelining be done with topic-style semantics being desired (let alone elsewhere in the language).

I'm aware topic-style stuff is usually the domain of preprocessors if it's not natively supported in the language regarding the latter assumption you pointed out; I've explained my case for the former two assumptions extensively. I understand that those assumptions have been quite contentious between the current champions and active members of the JS community that found time to comment/react to this repo and social media channels such as Twitter.

Good to hear from you again after quite some time nonetheless, @mAAdhaTTah.

tabatkins commented 2 years ago

I've made the same argument multiple times. All existing uses of pipeline-like structures in today's JS use tacit-style, because that's the only way to do something pipeline-like in Javascript.

Maybe it's also a better match for most JS programmer's brains and use-cases. But you cannot make that argument just from the observation that existing pipeline-like stuff uses that pattern, because they're forced to (unless they use a compile-to-JS language instead).

Regardless of all this, tho, we are not reopening the "should this proposal be for a tacit-style pipeline". That's been argued to death and decided, so please stop attempting to relitigate it. I don't mean to be mean here, but every argument you're making has been made in previous threads by other people. We know and understand the tradeoffs, have extensively documented them, and are proceeding with the proposal as written.

The original purpose of this thread outside of relitigating the pipeline style issue was to ask about the viability of using |: as the operator for this proposal. I believe that question has been conclusively answered - there's no particular reason to do so, as |> is reasonably well-established as being for pipelines of any variety across languages, and there's no competing claim for that operator or related syntax that we feel is worth adjusting for.

As that question has been answered, and the thread has instead repeatedly returned to the resolved issue of which pipeline style this proposal should be (despite me repeatedly requesting that it stay on topic), I am closing this issue.

mAAdhaTTah commented 2 years ago

I know this thread is closed now, but I started writing this a few days ago.


hack-style's latent premise that topic-style is better and its semantics would have been better received than tacit-style if it was equally feasible for developers to implement topic-style constructs in the language. ... I'm not convinced JavaScript's current syntactical affordances only enable tacit-style pipelines is a factor here since the demand for more tacit means of programming in JS has lead to some of the most popular JS libraries

But like... this is the whole thing. If alternative pipeline forms are impossible, of course JS would adopt tacit-pipelining, and of course the libraries that build on top of those tacit pipelines would be the ones to get popular. There is nothing else to build on. The logic is circular and isn't evidence that tacit is more popular or better than topic because topic is literally impossible. The lack of alternatives has to be a factor; how can it not be?

As a particular counterfactual, the pipeline in Julia by default functions closer to tacit-style but the first result on Google for "julia pipe operator" talks about how limited this is and points the reader to a macro that enables a topic-style approach. If JavaScript had macros, I don't think it's a given that tacit-style pipelining would be the preferred approach, given how much of the JavaScript standard library simply isn't built for it, and I think more generally, functional programming in JavaScript would also look quite different.

To bring all this back to the original thread: all of this suggests strongly, to me, that tacit-style pipelining does not have a monopoly over the |> sigil in particular, or pipelining in general, that would force us to adopt something else (e.g. |:) for topic-style. Functional JavaScript has its preferences, given the affordances of the language as it exists, but those preferences aren't universal within the language in particular or even functional programming generally (e.g. Elixir pipeline's first-arg insertion semantics), and I expect those preferences to change if the affordances change.

Good to hear from you again after quite some time nonetheless, @mAAdhaTTah.

Thanks. I've dropped off of these threads mostly because I'm not a champion, my opinion doesn't matter, and after arguing about the operator for 6 years, initially arguing for F# style pipes, and having worked on deeply functional code to the more standard-ish React ecommerce project I work on now, I'm very confident in the arguments for Hack over F# style pipes in JavaScript, and I don't have the energy to relitigate the arguments.