Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate (complementary) F# pipeline proposal? #202

Open
mmkal opened this issue Sep 11, 2021 · 51 comments
Open

Separate (complementary) F# pipeline proposal? #202

mmkal opened this issue Sep 11, 2021 · 51 comments
Labels
follow-on proposal Discussion about a future follow-on proposal

Comments

@mmkal
Copy link

mmkal commented Sep 11, 2021

As someone who was firmly in the F# and partial application camp, I'm very interested in the teeny section at the end of the readme called Tacit unary function application. Between that and +> I think bases would be really well covered. I don't feel that way about Hack alone, but still think it's fantastic that some form advanced to Stage 2.

Is the plan to include |>> in this same proposal? As a separate one with the same champion(s)? Is there anything that community members can do to maximise its chances (ideally at the same time, so implementers can handle both together)?

@mmkal mmkal changed the title Tacit unary function application proposal? Separate F# pipeline proposal? Sep 12, 2021
@kiprasmel
Copy link

kiprasmel commented Sep 12, 2021

What you're referring to is the Proposal 3 - Split mix - it is the better option to the Proposal 2 - the current Hack proposal.

It is, however, still flawed, because there is an even better option:

F# + The partial application proposal:

With it (partial application + F# pipes), you get the same functionality as you'd get with Hack, but 1) without the need of an additional operator |>>, and 2) the partial application functionality would work outside the scope of pipeline operators (meaning the whole language), which is great, as opposed to the special token of Hack that only works in the context of pipeline operators, 3) all other benefits of F# over Hack.

example - F# + partial application:

const multiply = (x, factor) => x * factor;

[1,2,3]
 |> multiply(?, 2)

// which just de-sugars to:

[1,2,3]
 |> (temp1) => multiply(temp1, 2)

it looks the same as Hack, and indeed is the same as Hack! What it does is curries out the argument marked with ?, and the F#'s pipeline operator calls that curried function.

But, while being the same, this proposal has an added benefit that the partial application operator would work anywhere in the language:

const multiply = (x, factor) => x * factor;

// can use directly - just like Hack:
[1,2,3]
 |> multiply(?, 2)

// can create a curried function
const multiplyBy2 = multiply(?, 2);

// and can use it the FP way, without needing an extra operator `|>>`:
[1,2,3]
 |> multiplyBy2

[1,2,3]
 .map(multiplyBy2)

// everyone is happy!

which is, as already mentioned, considerably better than the Hack + |>> proposal.

Why was it not considered? I don't know. In my view, this is the best that can happen with pipeline operators, meanwhile what we currently have in Stage 2 is the worst - even worse than no pipeline operators at all.

Why was Hack + |>> not considered (the proposal 3)? See [1]. Apparently it's because it's 2 operators and no browser vendors want to implement it so nobody championed it, which seems ridiculous because with Hack we're adding 2 operators too, and 3 if we also include |>>.

This is why I feel the whole thing is rushed. One part of why TypeScript ended up so good is because the guy who designed C# also participated in designing TypeScript. With pipeline operators in JS, it becomes more and more obvious to me that there were simply not enough FP-competent people in the TC39 committee, especially from different languages outside JS like Haskell, F# etc. - if there were, there's no way we would've ended up choosing the Hack proposal, especially without |>>.

And some are saying that JS is not necessarily an FP language. I disagree. History repeats itself. The same thing has happened many years ago, when NodeJS chose to use callbacks instead of promises. This is one of the regrets of Ryan Dall, the creator of Node. The arguments were the same as we're having right now, the reasons behind it were the same too. Let's not make the same mistake again.

And even then, with the F# + partial application proposal, nobody's forcing FP down your throat, because you get to choose yourself. This is not the case with the currently Hack proposal, and it is the case with Hack + |>>, though worse because of reasons alreay mentioned.

[1] https://github.com/tc39/proposal-pipeline-operator#tacit-unary-function-application

@mmkal
Copy link
Author

mmkal commented Sep 12, 2021

Yes, I'm essentially talking about reviving Split Mix, now that Hack pipes has achieved consensus as a direction from the committee.

The line:

Requires browser vendors to agree to implement two similar pipe operators, so nobody is currently backing this proposal

Was written quite a while ago, and I'm hoping things have changed sufficiently that it can be revisited. Specifically, without consensus on one operator, I can see why it would seem risky to put all eggs in a two-operator solution basket. Now that Hack pipes have consensus, hopefully the door is open to at least trying to advance another operator. If it fails, or is delayed, Hack could advance ahead of it - that would be a shame but still better than nothing.

@kiprasmel I don't want to get into debate about Hack vs F# here either (partly because I personally also would have preferred F#). The premise of this particular GitHub issue is acceptance of Hack as the "winner", the topic is whether F# pipes should be a separate proposal or not. As I suggested in the other thread, maybe you should make a new issue in which you can try to make the argument for abandoning Hack altogether. It's off-topic here.

@kiprasmel
Copy link

kiprasmel commented Sep 12, 2021

@mmkal thank you, good point, I will make a separate issue.

Though, currently it seems like the trade-offs are being discussed in a gist outside this repo, by a TC39 member who is opinionated towards Hack - I'm not a fan of it being outside this repo:

https://gist.github.com/tabatkins/1261b108b9e6cdab5ad5df4b8021bcb5

Update: see #205

@peey
Copy link

peey commented Sep 13, 2021

It would be good to have separate places for discussion of hack and F#-style proposals.

I believe that earlier hack-style was being discussed at https://github.com/js-choi/proposal-hack-pipes/

Should F#-style proposal related discussions happen at https://github.com/valtech-nyc/proposal-fsharp-pipelines instead? Why is there no link to that in the readme anymore?

Is there no one who's championing the F#-style proposal?

@mmkal mmkal changed the title Separate F# pipeline proposal? Separate (complementary) F# pipeline proposal? Sep 13, 2021
@mmkal
Copy link
Author

mmkal commented Sep 13, 2021

@peey interesting, I didn't know about that fork. It looks like it hasn't been updated in a while, and if it's to be made complementary to this proposal (which is, at time of writing, Hack), it should remove all special cases for await and change the operator to |>>. It might be easier to write a new one because |>> is so simple - everything messy can be handled by |>. It looks like @mAAdhaTTah was involved in it - any thoughts?

@mAAdhaTTah
Copy link
Collaborator

@mmkal If a separate F# proposal is desired, you should create a new repo for it. That repo was for the competing F# syntax before Hack advanced to Stage 2.

@mmkal
Copy link
Author

mmkal commented Sep 13, 2021

@rbuckton I'd like to respond to your comment in #91 (comment) since it directly relates to the topic of this issue:

I have been a staunch advocate for F#-style pipes since the beginning, but it's been difficult to argue against the groundswell in support of Hack-style pipes due to some of the limitations of F#-style pipes. If we kept debating on F# vs. Hack, neither proposal would advance, so my position as Co-champion is "tentative agreement" with Hack-style. Both sides have advantages and disadvantages. F#-style feels to be a better fit for JS to me, especially given the existing ecosystem, and meshes better with data-last/unary producing libraries like Ramda and RxJS. F#+PFA works well for data-first libraries like underscore/lodash. However, F#-style is harder to use with yield, await, and methods where the topic is the receiver (i.e., ^.method()), which is an advantage for Hack-style.

So we were stuck at impasse. I don't think a solution that mixes both is tenable, and I'd venture to guess that having multiple pipe operators (one for each) may not make it through committee. As what feels like the lone dissenter against Hack-style on the committee (or at least, the only vocal dissenter specifically in favor of F#-style), I'd rather not stand in the way of the feature advancing at all, despite my reservations, because I think it's a valuable addition regardless of approach.

If we could find a way to get two pipe operators through committee, I would support that. JS wouldn't be the first language with multiple pipe-like operators. 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.

Are there specific people (on the committee, or browser implementors) who are strongly against multiple operators? If so, maybe they could be invited to speak up here, since there is clearly huge interest in F#-style pipes (documented in #205) - so an offhand dismissal of multiple operators would be very disappointing: the use cases are real, clear, very popular and there's well-established precedent in other languages.

(A note about popularity - I agree that it would be a bad idea to just make decisions based on GitHub upvotes, but the volume, passion, and popularity of arguments in favour of F# is significant and shouldn't be ignored. Which is why even if there's some resistance to more operators, it's worth pushing IMO. The suggestion isn't one that's made capriciously.)

To make this more of a practical and direct question - @rbuckton as a committee member, do you think a new complementary proposal repo should be created so that this one can focus on Hack? CC @tabatkins @js-choi and @ljharb - would be interested to hear your thoughts on this too.

@tabatkins
Copy link
Collaborator

A number of people on the committee are just barely on board with pipeline in general, and specifically found smart-mix to be too complex of a mental model to be worth supporting. I suspect that presenting the same two-version syntax via a pair of operators would bring up the same complaints. (This was never seriously advanced as an option during committee meetings, however, so I'm not 100% sure; this is my intuition only.)

In general, tho, speaking as a language designer with a decade+ of experience, having two distinct features that are only very slightly different is almost always a mistake. You usually want clear, bright lines separating features, so people know when it's appropriate to use each feature. Subtle differences tend to bring confusion instead; it's almost always the right choice to instead just choose one variant and let the other use-cases be slightly inconvenienced. (Or, sometimes, not doable at all; you want to avoid locking out use-cases as much as possible, but good design sometimes has to accept that as a tradeoff.)

Sometimes that's not the best choice, if there are multiple use-cases that are roughly equally vital and the inconvenience of doing one in the other's version of the feature is significant. But that's usually not the case, and definitely not here - as has been argued a number of times, even if you have a library built on returning unary functions, using them in a Hack-style pipeline just requires appending (^) to the end of the function.

So, as a champion I would be pretty strongly against a two-operator solution, where the two are precisely identical in powers, but just call their functions slightly differently.

@tabatkins
Copy link
Collaborator

That said, if anyone wants to pursue any of these ideas on their own, I have neither the power nor the desire to stop you. ^_^

@js-choi
Copy link
Collaborator

js-choi commented Sep 13, 2021

I am personally pro-eventually-adding-a-split-mix tacit-unary-function-call |>>. I agree with @rbuckton (#91 (comment)) that |>> is a reasonable goal.

JS wouldn't be the first language with multiple pipe-like operators.

However, @tabatkins is right in that the committee was barely supporting any pipe operator (there are still some delegates that argue for no pipe operator at all). That was much of the reason why any pipe operator was stalled for such a long time (before the State of JS 2021 results showed that lots of people requested a pipe operator). (I plan to add a history.md timeline to this repository within the next few days that talks about this in more detail.)

I’ll copy and paste some parts from what I said here: tc39/proposal-hack-pipes#18 (comment)

Whether to reintroduce split mix (|>>) and whether to do it now or later

The second thing to address is whether and when we should try to reintroduce what we have called “split mix”: a separate pipe operator |>> for tacit unary function calls. This would allow people using data-last / curried-function styles to omit (^).

I think @tabatkins has said before that also having |>> might be a hard sell to the Committee, because from JavaScript’s perspective x |> f(^) is equivalent to x |>> f under this paradigm […]. But |>> is something that I would be happy to work on…

…It’s just that the Committee may well balk if we do too much at once. It’s already having a hard time swallowing any pipe operator. It generally prefers to do things piecemeal, while keeping forward compatibility with future extensions. And Hack pipes are forward compatible with |>>.

My inclination (@tabatkins, @ljharb, @mAAdhaTTah, others are free to insert their own opinions) therefore is to keep this first pipe proposal focused on Hack |>, and to leave |>> to a later proposal (if |> ever even gets accepted, as we hope).

(Aside: I would like to push back at “these FP libraries represent the majority of the pipe function usage that the pipeline operator is supposed to replace”. Although unary function calls are a significant use case, from this proposal’s perspective, unary function calls are not the most common use case. The pipe operator is not supposed to replace only unary function calls; it is supposed to replace deeply nested expressions in general, which occur in all APIs. In the worst case, people using Ramda/RxJS/etc. can keep using pipe functions for curried-function calls…while still finding Hack pipes useful for interoperation with other APIs’ data-first function calls, function calls with trailing option objects, Web APIs, and so on.)

Anyways, I’m personally enthusiastic about |>> for tacit unary function calls and, in fact, I’d be eager to eventually write a proposal for |>> myself. (Clojure is one of my homes, and Clojure has a ton of threading macros, so I’d be used to having two pipe operators.) It’s just that the Committee may recoil from too big a bolus of syntax at once…It’s already been hesitant at even one pipe operator. If there’s enough of a use case for |>> (e.g., working with TypeScript’s limited type unification before microsoft/TypeScript#30134 lands), then it can fight for it on its own merits. I would be happy to try to make that fight for it, later.


I will say that partial function application may definitely coexist with Hack pipes—either in the form originally proposed by @rbuckton (which has eagerly evaluated partial arguments, once before any function calls) or as Hack pipe functions (which are like arrow functions in that the partial arguments are evaluated lazily, at each function call).

I would be happy to fight for a partial-function-application syntax later, too.

@mmkal
Copy link
Author

mmkal commented Sep 14, 2021

@tabatkins @js-choi thank you - that's very helpful context.


A number of people on the committee are just barely on board with pipeline in general

Hmm. I'd be interested to learn more about this - are there any specific meetings that it'd be worth reading to understand this better in https://github.com/tc39/notes?

In general, tho, speaking as a language designer with a decade+ of experience, having two distinct features that are only very slightly different is almost always a mistake

There are many counterexamples to this in successful and popular languages, including F# which has |>, >>, <|, <<, as well as the ability to call functions like add 1 or add(1).


Also, I don't think this is true:

EDIT: THIS EXAMPLE IS WRONG

even if you have a library built on returning unary functions, using them in a Hack-style pipeline just requires appending (^) to the end of the function

const {memoize} = require('lodash')

const track = value =>
  value
    |> ^.toLowerCase()
    |> memoize(trackEvent)(^)

Behaves differently from this:

const {memoize} = require('lodash')

const track = value =>
  value
    |> ^.toLowerCase()
    |>> memoize(trackEvent)

In the first track('FOO'); track('foo'); will lead to trackEvent being called twice. In the second, once. In the case of memoize, it's probably the second one that I want. In other situations, it might be the opposite. Of course, there are workarounds, but it's a bit of a footgun. And the workarounds aren't always appropriate (above, it might not be desirable to add a const memoizedTrack = memoize(trackEvent) to the scope). This is much more than a three-character tax, the actual runtime semantics are different.


Another case, where it is just tax, but it's more significant than (^) at the end:

value
  |> ^.toLowerCase()
  |>> JSON.parse
  |>> ({ x, y, z }) => {
    const api = new SideEffectyAPI(x)
    api.initialize(y)
    return api.foo(z)
  }

vs

value
  |> ^.toLowerCase()
  |> JSON.parse(^)
  |> (({ x, y, z }) => {
    const api = new SideEffectyAPI(x)
    api.initialize(y)
    return api.foo(z)
  })(^)

Needing to wrap the arrow function in parens is a heavy, confusing, multi-line tax.

Granted, SideEffectyAPI here is probably not well designed, but it might be an external library that I can't control.

Note: do-expressions might mitigate this problem somewhat.

Lastly, even under "normal" circumstances the tax is sometimes just... lame. And there will always be some data-last unary functions. How weird and wrong the following looks will be a disincentive to using language-level syntax for pipelines and will lead to deep schisms, instead of encouraging almost all JS users to write in a similar way. Weird and wrong-looking example:

getUser()
  |> Either.fold(
    err => `Something went wrong getting your user info. ${err.message}`,
    user => `Hello ${user.name}`
  )(^)
  |> console.log(^)

I suspect FP users will just use their user-defined pipe functions, and other users won't use FP libraries' utilities, when it doesn't have to be that way.


That said, if anyone wants to pursue any of these ideas on their own, I have neither the power nor the desire to stop you. ^_^

Err... aren't you a committee member? Doesn't that mean you do have the power, since stage-advancement requires 100% consensus? Good to know that you don't have the desire, in any case!


@js-choi good to hear! It'd be great to know what you think the right timing would be.

@sandren
Copy link

sandren commented Sep 14, 2021

In general, tho, speaking as a language designer with a decade+ of experience, having two distinct features that are only very slightly different is almost always a mistake.

@tabatkins Does that mean the adoption of Hack-style would decrease the chance of partial application reaching Stage 4? If so then I feel that the Hack-style proposal should be evaluated against both F# and partial application rather than F# alone. I know that's not the norm for the TC39 process, but this proposal has been anything but ordinary.

Is there any chance that the committee would consider evaluating the pros/cons of advancing Hack-style against advancing both the F# and partial application proposals before any reach Stage 3? I think it would be helpful to consider if advancing both F# and partial application to Stage 2 simultaneously may be better for the JavaScript language than either pipeline proposal alone.

I also think doing so in earnest would help alleviate some of the contention around the pipeline operator in general regardless of the outcome. 😀

@ljharb
Copy link
Member

ljharb commented Sep 14, 2021

@sandren no, partial application has a lot of obstacles to work through on its own, and pipeline advancing as F# wouldn't help it much.

@js-choi
Copy link
Collaborator

js-choi commented Sep 14, 2021

good to hear! It'd be great to know what you think the right timing would be.

Does that mean the adoption of Hack-style would decrease the chance of partial application reaching Stage 4? If so then I feel that the Hack-style proposal should be evaluated against both F# and partial application rather than F# alone. I know that's not the norm for the TC39 process, but this proposal has been anything but ordinary.

And at the cost of a pipeline operator that will provide something new, and I assume at the cost of the partial application proposal.

I personally would support a syntax for partial function application.

I don’t think that the adoption of Hack pipes would decrease the chance of a PFA syntax. They can coexist.

However, I also think any syntax for partial function application (which I personally do support) would continue to run against pushback at the committee, regardless of what the pipe operator happens to be. Like @ljharb said, F# pipes advancing actually wouldn’t help PFA syntax that much.

So, although I’m somewhat hopeful for the future of PFA, I also have tempered expectations. This has little to do with Hack-vs.-F# pipes and more to do with other TC39 delegates’ concerns about PFA in general (like performance and aesthetics).

I’ll paste what I said in #207 (comment):

For what it’s worth, I think that partial function application could coexist with Hack pipes—either in the form originally proposed by @rbuckton (which has eagerly evaluated partial arguments, once before any function calls) or as Hack pipe functions (which are like arrow functions in that the partial arguments are evaluated lazily, at each function call).

I’d be happy to fight with @rbuckton for a partial-function-application syntax later. But it might be an uphill battle (and not because of Hack pipes). From what I recall, some people on the committee have always been against partial-application syntax (independently of pipe). I’m not one of them, but…

So, pushing for partial function application should probably wait, until some pipe operator would get further. The Committee can stomach only so much new syntax at once.

@lightmare

This comment has been minimized.

@kiprasmel

This comment has been minimized.

@mmkal
Copy link
Author

mmkal commented Sep 14, 2021

EDIT: THIS IS WRONG

@mmkal

const {memoize} = require('lodash')

const track = value =>
  value
    |> ^.toLowerCase()
    |>> memoize(trackEvent)
track('FOO'); track('foo');

Could you please explain how this would end up calling trackEvent only once? I can't see it.

@lightmare my apologies, you are totally right. Maybe this example makes more sense.

const logSlowResponses = threshold => value => {
  if (Date.now() > threshold) {
    console.log('slow response!', value)
  }
  return value
}

value
  |> fetchFromSlowAPI(^)
  |> logSlowResponses(Date.now() + 3000)(^)

👆 will never log whereas using |>> we can make sure it throws when the API was too slow, as expected:

value
  |>> fetchFromSlowAPI
  |>> logSlowResponses(Date.now() + 3000)

@lightmare
Copy link

@mmkal I still don't get it. For the second example to log "slow response!", you need Date.now called before fetchFromSlowAPI.

@lightmare
Copy link

Is this kind of what you're trying to express?

const pipe = (input, ...funcs) => funcs.reduce((x, f) => f(x), input);

pipe(
  value,
  x => fetchFromSlowAPI(x, xyz),
  logSlowResponses(Date.now() + 3000),
);

@mmkal
Copy link
Author

mmkal commented Sep 14, 2021

EDIT: THIS IS WRONG

@lightmare yes, that's basically it. I think I got my example wrong again. I've updated my example to consistently use |>>. That is, I changed |> fetchFromSlowAPI(^, xyz) to |>> fetchFromSlowAPI (since the xyz was made up anyway).

So, the updated example is

value
  |>> fetchFromSlowAPI
  |>> logSlowResponses(Date.now() + 3000)

Since |>> is just an operator which is operating on functions, logSlowResponses(...) is called before fetchFromSlowAPI(...), and the curried function return value logSlowResponses(...)(...) is called after.~

@lightmare
Copy link

@mmkal Ok thanks, now I understand. It won't work that way, though:

Since |>> is just an operator which is operating on functions, logSlowResponses(...) is called before fetchFromSlowAPI(...), and the curried function return value logSlowResponses(...)(...) is called after.

Every binary operator in JS evaluates its operands left-to-right.

The F# pipeline operator has to be left-associative. x |>> f |>> g is equivalent to (x |>> f) |>> g, but x |>> (f |>> g) is something completely different.

Going back to your example:

value
  |>> fetchFromSlowAPI
  |>> logSlowResponses(Date.now() + 3000)

The order of evaluation is:

topic = value
topic = fetchFromSlowAPI(topic)
topic = logSlowResponses(Date.now() + 3000)(topic)

@mmis1000
Copy link

mmis1000 commented Sep 14, 2021

value
  |> ^.toLowerCase()
  |> JSON.parse(^)
  |> (({ x, y, z }) => {
    const api = new SideEffectyAPI(x)
    api.initialize(y)
    return api.foo(z)
  })(^)

I through this is probably better handled by do expression proposal instead.
Juts like F# pipe + partial function
Hack pipe + do expression is probably another good combo at preserve proper reading order

value
  |> ^.toLowerCase()
  |> JSON.parse(^)
  |> do {
    let { x, y, z } = ^
    const api = new SideEffectyAPI(x)
    api.initialize(y)
    api.foo(z)
  }

BTW.

The kotlin actually allow similar usage I stated above (mixing statement and expression in a long chain).
The above can actually be written in valid kotlin with minor change.

value
  .let { it.toLowerCase() }
  .let { JSON.parse(it) }
  .let { (x, y, z)->
    val api = SideEffectyAPI(x)
    api.initialize(y)
    api.foo(z)
  }

(I am not saying it is a good or bad practice to write wildly long chain. Just say there are already some languages allow so)

@js-choi
Copy link
Collaborator

js-choi commented Sep 14, 2021

I through this is probably better handled by do expression proposal instead.

See also tc39/proposal-hack-pipes#4 (which led to me rewording some stuff in the explainer a few months ago).

@tabatkins
Copy link
Collaborator

Yup, a few people have addressed it now, but I'll stress again that val |> foo(^) |> bar(^) (Hack-style) and val |> foo |> bar (F#-style) are precisely identical in every observable aspect of their execution - timing, ordering, etc.. You need to examine the AST to tell they're different.

The confusion is likely stemming from the fact that both are different from val.pipe(foo, bar), because functions evaluate all of their arguments before beginning execution. So in this case, the foo and bar expressions are both evaluated before you start piping to either of them, while in F#-style the foo expression is evaluated, then piped to, then when it returns the bar expression is evaluated.

This is the same as foo() + bar() + baz() - foo() and bar() are evaluated, then they're added together (potentially invoking their .valueOf() or .toString() methods, which can be observable and/or slow), then baz() is evaluated and added to the result. Operators work a little bit differently than functions, is all.

@tabatkins
Copy link
Collaborator

An example showing off the above:

function foo(v) { 
	console.log("call " + v); 
	return {
		valueOf: ()=>{
			console.log("value of " + v); 
			return v;
		}
	}
}

foo(1) + foo(2) + foo(3)

// logs:
call 1
call 2
value of 1
value of 2
call 3
value of 3

@ljharb

This comment has been minimized.

@SRachamim

This comment has been minimized.

@mohaalak

This comment has been minimized.

@ken-okabe

This comment has been minimized.

@ken-okabe

This comment has been minimized.

@mAAdhaTTah

This comment has been minimized.

@js-choi
Copy link
Collaborator

js-choi commented Sep 18, 2021

Heya, hey, everyone. (And in particular, @stken2050, since you addressed me directly.) I appreciate everyone’s input here.

First, I want to reiterate my apologies from #202 (comment) and #206 (comment). I apologize that pursuing Hack pipes isn’t what many of you expected or wished for. I myself am enthusiastic about bothF# pipes and PFA syntax, and I plan to fight for both in TC39 in the future—but neither F# pipes nor PFA syntax have ever faced very good prospects (see #221). So, for now, we have been trying to focus on something that would help the entire ecosystem (particularly Web APIs).

As someone on the champion group who is enthusiastic about pipes (both Hack pipes and F# pipes) and PFA syntax, I—and everyone else on the group—appreciate everyone’s input and have been taking your comments seriously. (Though keep in mind that a lot of these comments echo stuff that we’ve been talking about since 2017, and there are a lot of constraints from TC39 representatives outside of the champion group.)

Although I appreciate these comments, a lot of them aren’t really on topic for this particular issue, which is specifically about the possibility of following up Hack pipes with a separate F#-pipes proposal (as well as @rbuckton’s PFA-syntax proposal.

Based on a lot of the comments I’ve seen over the past few days, I’ve opened several new issues, and I’d like to direct you all towards those issue and away from this issue. These include:

My sincere apologies if I marked your comment as off topic! I know it feels, to many people, as if your concerns are being blown over. But this thread is already super long, there were many comments were about many different topics, and any incoming readers would have a difficult time following the thread of this issue’s original topic. I invite you all to take your concerns to the other threads, and help out with improving the explainer in the other issues—if you feel like it! My apologies again, and thank you again. We’re all better for your input.

@ken-okabe

This comment has been minimized.

@js-choi

This comment has been minimized.

@ken-okabe

This comment has been minimized.

@ken-okabe

This comment has been minimized.

@js-choi

This comment has been minimized.

@js-choi js-choi added the follow-on proposal Discussion about a future follow-on proposal label Sep 19, 2021
@js-choi
Copy link
Collaborator

js-choi commented Oct 5, 2021

Like I mentioned in #221 (comment), I’m going to present a Stage-0 proposal for several new Function convenience methods to the Committee at its next plenary meeting. It’s called proposal-function-helpers.

That proposal’s new convenience methods would include standard Function.pipe and Function.flow functions.

To repeat #221 (comment), the pipe champion group has presented F# pipes for Stage 2 twice to TC39, being unsuccessful both times due to pushback from multiple other TC39 representatives about multiple concerns. (For more information, see #221 and HISTORY.md.)

Given this reality, the Committee is immensely more likely to pass a Function.pipe helper function than a similar syntactic operator.

Standardizing a helper function does not preclude standardizing an equivalent operator later. For example, the Committee standardized binary ** even when Math.pow existed.

In the far future, we might try to propose a F# pipe operator, but I would like to try proposing Function.pipe and flow to the Committee first, in an effort to bring their benefits to the wider JavaScript community as soon as possible. First steps.

Please feel free to read proposal-function-helpers’ explainer and leave feedback if you want.

@Inori-Lover
Copy link

it is not good, that one lang have two style to achive same purpose

@lightmare
Copy link

it is not good, that one lang have two style to achive same purpose

In general, that is not true. Synonyms. Syntax sugar. Built-in for-each loops versus functions, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
follow-on proposal Discussion about a future follow-on proposal
Projects
None yet
Development

No branches or pull requests