-
Notifications
You must be signed in to change notification settings - Fork 107
Description
@tabatkins has put together this wonderful essay that weighs the differences between F# and hack style pipes, and ultimately concludes that the differences are minor, but hack-style is a little better, in part due to the fact that for most use cases, hack-style will be more concise.
I've got to agree with @tabatkins on all of this, however, as I mentioned in a comment over there (that I decided to bring up over here), there's a minor thing we can add to F#-style pipes, to have it reap almost all the same benefits as hack-style (all but the nice support hack-style has for await, but I'm ok having a more verbose solution for that).
But first - some background.
What we're aiming for is to find a simple, yet easy-to-read version of the pipeline operator. With that in mind, I want to make a comparison between two fictional pipeline operator syntaxes - |next> and % @.
id |next> ? + 1 |next> groupsFromUser(?, true) |next> rolesFromGroups(?)
id % @ ? + 1 % @ groupsFromUser(?, true) % @ rolesFromGroups(?)What I want to point out from the above example, is how much more readable the |next> version is. Why? Because it's a single unit. % @ is two separate things, which adds a lot of visual clutter and makes it harder to glance at the expression and see what's going on. This is the issue that F# style pipes currently has: more often than not, an F# pipe requires a function literal afterwards, causing you to write three distinct "units" in order to do anything useful - the "|>", an identifier, and "=>":
id |> x => x + 1 |> u => groupsFromUser(u, true) |> g => rolesFromGroups(g, false)Gross 🤮️
So what's the one thing we can add to F#-style pipes in order to save it? We can popularize the new "(Eiffel) tower" pseudo operator |>$=>, which combines those three units into one, and effectively brings the benefits of hack-style pipelining into F#-style. Notice that |>$=> is nothing more than the pipeline operator, an identifier, and "=>" smashed together into a single unit, but it greatly improves the readability of code. Here's the same example, using the tower operator:
id |>$=> $ + 1 |>$=> groupsFromUser($, true) |>$=> rolesFromGroups($, false)You may argue that |>$=> is such a loooong operator, but remember that our goal isn't to reduce character count, it's to improve readability. If we wanted to write more concise code, we would just do what we can already do today - remove the pipeline operator and call the functions in a normal fashion. That will always be more concise than any pipeline operator proposal we come up with.
Here's some more examples, adapted from the README:
let newScore = person.score
|> double
|>$=> add(7, $)
|>$=> boundScore(0, 100, $);
getAllPlayers()
|>$=> $.filter( p => p.score > 100 )
|>$=> $.sort()
|> Lazy
|>$=> $.map( p => p.name )
|>$=> $.take(5)
|>$=> renderLeaderboard('#my-div', $);I love the power of the hack-style operator, but I also love the simplicity of F#-style (we're not having to add a special topic concept that's unique to a single operator). I know it's not conventional to do this kind of thing, but I advocate that we run with F#-style and attempt to popularize this tower operator with it (we can include it in the proposal's README, eventually put a page about it on MDN, etc), thus giving us the best of both worlds.