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
The |>
operator would be great for function composition instead. Explanation...
#50
Comments
With the current proposition, you can write const doubleThenSquare = value=>value |> double |> square; You can also write const result = 42 |> double |> square; With your change, this second example would become: const result = (double |> square)(42); In my opinion, the extra parenthesis are an issue. It's easier to compose functions, but chaining calls would be less maintainable. const map = f => function*(iterable) { for(const v of iterable) yield f(v); };
const tap = f => function*(iterable) { for(const v of iterable) {f(v); yield v;} };
const toArray = () => iterable => [...iterable];
const filter = f => function*(iterable) { for(const v of iterable) if(f(v)) yield v; };
const result = (
filter(x => (x % 2 == 0))
|> map(x => x * 2)
|> tap(x => {
console.log(x);
})
|> toArray()
)([1,2,3,4,5]); |
Not sure I agree that chaining calls would be less maintainable: Current proposal provides an "alternative syntax for chaining calls", which can lead to inconsistent code where some people choose Also it is worth considering the use cases for such constructs. Is it more useful to create reusable composed functions or execute the composition in place? I would argue that having the terse syntax for reusable function composition adds a whole new dimension of expressive power to the language, whereas the proposal doesn't save any coding complexity as such over |
FWIW, this example: const result = (double |> square)(42); isn't that different from how you have to do it now using a const result = pipe(double, square)(42); so there are some parallels to current practice with @TheNavigateur's suggestion. In my experience, a function composition is rarely called where it's defined; that suggests the syntax that favors composition over evaluation might be better for the language. |
I am not sure how the semantics for this should work. Do you want |
No, always function composition |
If it's always function composition, that would make the normal use case of |
I believe that's the intention. I would argue the use case of calling inline is less common than defining and executing functions separately. |
Yes. |
I'm not an expert on language design, but if I may weigh in... Both directions of the proposal do things that can already be done in the language...
The prior art cited in the proposal README suggests that several existing functional languages use the In my opinion, the proposal as written benefits everyone - functional and imperative styles alike - in fact, it can help imperative style developers begin to adopt a functional style; imagine if jQuery were written with this syntax instead of creating custom objects to allow chaining. Changing it to a function composition operator only benefits functional-centric developers. To that point, I strongly disagree with @mAAdhaTTah's suggestion that "the use case of calling inline is less common than defining and executing functions separately"; this has not been my experience with JavaScript at all. I think function composition is great, but please don't hijack a good proposal like this! How about a separate operator? Or even just including a native *shameless plug: https://github.com/phunware/pipeline |
I disagree that changing it to a function composition operator only benefits functional-centric developers. Consider the following use case for declaring no-param, no-result functions to be executed in order: Composition operator:
This is beautiful chaining of imperatively ordered functions in a short syntax. Consider the equivalent for the immediate execution operator:
(This already highlights the logical inconsistency in having Now let's look at immediate execution for the same: Composition operator:
Immediate execution operator:
I would argue that the composition operator approach is preferable for imperative style development too. Reusability, readability, intuitiveness |
Fair; that's likely specific to functional programming, which is what I expect the proposal is likely to be most popular/useful to. |
@dallonf "never seen and ended up writing myself"? http://ramdajs.com/docs/#pipe I'm too lazy to search for more. Or did I misunderstand what you were driving at? |
There's clearly a use case for both of these, and you can definitely use one to get the other, at the cost of a little bit more syntax. The question is more like, which one is more common and intuitive. |
@jasmith79 All three of the examples you gave are for function composition, ex. To other responses... to be clear, I see the value in function composition. But I see it as a tool to solve a different problem than the pipeline operator is intended to solve, so I believe deserves its own proposal. (I would be strongly in favor of making that However, this discussion is not a matter of proving that function composition is useful, or even that it is more useful; in order to turn the immediate execution pipeline operator proposal into a function composition proposal, you would have to determine that immediate execution has no value at all and has no place in the language, which I do strongly disagree with and I believe I am backed up by the fact that immediate execution pipeline operators can be found in F#, OCaml, Elixir, Elm, Julia, Hack, and LiveScript (I copied that list from the README - but I'll also add Clojure's equivalent). |
I don't think it's a question of whether immediate execution has "no value at all". The composition operator version achieves immediate execution with mere parentheses:
The converse is not true: doing composition with the immediate execution operator requires The other issue, which maybe you can address, is the unintuitiveness of expression arguments |
I just had another thought. If you want to compose an array of functions, identified e.g. from server side data, in a loop you could simply do:
as an analogy for how
...with a composition operator. |
I strongly disagree with this characterization of the argument. It's not that immediate execution has no value at all; it's that the value of immediate execution for this particular syntax is lower than using said syntax for composition. I'm happy to have both, but if we wanted both, we'd need either a way to disambiguate between the two intents for this syntax, or an alternative syntax for one of the options. To my mind, wrapping the (switchOnEngine |> drive)(100) already is that alternative syntax for immediate execution. By treating the |
I would also point out that the languages you point to with the To counter-point my own argument, using Counter to the counter: I much prefer piping over composition; right to left makes more intuitive sense to me, personally, and I pretty much exclusively use Lastly, on deeper thought, the iteration example is quite cool. Would make for some clean implementations fpr generating new functions w/ |
Both proposals have an "ugly" use case when you use them for the other. As-written, you have to use an arrow function to make composition work with the You're proposing that |
I'd consider them equally ugly, personally, but the composition use-case more consistent (everything is a function; the first param isn't unique) and more useful (imo, obvs; I see declaration/ execution separation as more common in fp style). Also |
👍 Your proposal seems unrelated to this one except for the fact that you want to use the same character combination. Given that that's a pretty small thing and that there's ample precedent for using |
I think the Consider a case where you mix classic dot-chaining with piping:
The fact that the input comes first (which @TheNavigateur keeps calling out as unintuitive) is important to this proposal. It's the whole point of it, really: the value flows through the functions in reading order. Again, I re-iterate that we need two different operators. Or an operator and a function. Or two functions. Two proposals, in any case. Function composition and value piping are not the same problem and trying to fix both with one is going to leave one or both problems with a suboptimal solution. Given the prior art for Which leads us to, again, a new proposal for function composition. This is also a feature with lots of prior art and a proven value - particularly in the languages that have a pipeline operator! (This is evidence for the value for having both.) Those languages, unfortunately, tend to use Or, perhaps you do take |
Well said by @matthewwithanm and @dallonf. From the readme of this repo:
If there is a new proposal for function composition to be added to the language as a function or operator, I would happily support and follow it. But that is not what this proposal is. I would encourage anyone interested in pursuing this further to create a new proposal repo. Pasting a link to that proposal would be appropriate. I'll close this topic in a little bit barring some unexpected input, but it would be good if this topic could be closed out with a link to the new proposal or discussion. I think there is a lot of overlap between people following this proposal and people who would be interested in a function composition proposal. |
Allowing
where I consider this a significant enhancement to this proposal. |
I could back a second, separate proposal like Perfect is the enemy of good. |
Given the above comments, I'm in favor of the current proposal. Definitely would be interested in working on composition as well. |
Should both Can, then, the
syntax be sustained in light of 2 things:
? I would argue not, and so surely either |
I'll close this now, as the consensus seems to be that a pipe operator has no reason to be complected with function composition, but a link to a function composition proposal or further discussion in another forum would still be appropriate. |
I would argue not, and so surely it would become part of this proposal |
TehStrike I pressed close by accident. I belive this very much open as it would impact the contents of this proposal |
We could make this valid syntax if we proposed a composition operator that made it work thus: 100 |> startEngine +> drive +> useCruiseControl
// same as
const startCart = startEngine +> drive +> useCruiseControl
100 |> startCar if we propose |
Just so, as @mAAdhaTTah and @TehShrike said. The hypothetical binary functional-composition operator Whether That question will depend on which of the latter two expressions is more generally useful as the default affordance for programmers. But that is purely a syntactic question for the proposal that will come later in time: I personally would love to see both. |
Of course Then What I'm really saying, is that this proposal should be simplified to |
I'm not seeing the difference, at a low level, between a proposal that only allows |
You got me. My mistake I had not thought about it properly |
OK I've updated the proposal to Please provide your feedback. Cheers, N |
Just wanted to chime in and say that I both love functional programming and never seen a programming style where functions are composed and prepared in advance even when they're only used in one place after working on dozens of apps. Seems like a huge maintenance burden to me, trying to reuse things at all cost. On the contrary, using anonymous functions and pipelines is by far the more common case in JS land. |
It's called point-free style and very widespread in FP languages. For example, it's very popular in Haskell and other ML inspired languages. It can make the code more terse and allows you to document your intent, since you can name the function that's the result of composition, even in the local scope. |
The existing code
would be rewritable as:
Reasons to prefer this instead of
|>
being a function call operator:FunctionExpression |> FunctionExpression
instead ofInputExpression |> FunctionExpression |> FunctionExpression
)Fuller explanation here:
https://github.com/TheNavigateur/proposal-pipeline-operator-for-function-composition
https://esdiscuss.org/topic/native-function-composition
The text was updated successfully, but these errors were encountered: