Sometimes I'm asked why we don't have "dot-chaining" in RxJS anymore, or why RxJS made the switch to use pipe. There are a lot of reasons, but this...
For further actions, you may consider blocking this person and/or reporting abuse
Real world use case: slugify a String:
Very cool article!
I gave an attempt at implementing your proposal for the slugify function if anyone cares to play around: codesandbox.io/s/js-functional-pip...
Forgotten
(s)
at the end?Yes. It was like pseudo code
We can create another function to let
pipe
even more readable than pipeWith with the following function:Or use the curried pipeline where you can pass your arguments directly.
This API really resonates with me. Thanks for sharing.
Not a fan, this breaks functional composition
Awesome first post Ben, welcome! Sorry I grabbed the premium username space.
It looks like the pipeline in general provides a really convenient and reusable syntax, also battle-proof from decades of use in Unix and other languages. But the pipeline proposal seems to get stale with multiple disagreements and can take years to complete.
The
pipeWith
function keeps the linear flow but mixes arguments with functions inside its arguments and requires to begin with array:Why not instead use a curried pipeline function with identical functionality:
Among advantages: no need to wrap arguments in an array and typing the comma is quicker than "
|>
" that requires 2 Shift + 2 keys (4 in total vs 1 for the comma)?I have been searching for cleanest and most reusable patterns to write and compose Continuation-Passing-Style functions and this
pipeline
curried function seems to do "the best job", but I'd be curious to put this claim to test and hear other thoughts.From one side it is sad to have to wait so long for this improvement, but from the other side it is better to wait than to have a modification in the language syntax that doesn’t work well in the future.
Your efforts to find the perfect syntax are definitely valuable, but I think it requires a lot of analysis of the consequences to avoid superficial argumentation. Removing the need to wrap arguments in an array doesn’t necessarily makes things easier, because it is more likely that the argument being piped will be already coming as a variable from somewhere else. So we would rarely construct arrays to pipe, but pipe some existing array. And in this case you would need to spread the array to be able to use it in the pipe. Besides that, the pipeline operator can be used with anything, not only arrays. It is just a different way of writing nested function calls.
Also, the amount of keys pressed to type the code is not that important, because we spend more time reading code than actually typing, so maybe less parentheses is preferable instead of one extra character.
Not data last.
Why is data last important? If the function composition grows (multi-line), it can be annoying to go to the end just to see what is passed in at the beginning. Chronological ordering is more readable. If it doesn't pose a clear disadvantage, I don't see why not use it.
From what I understand of functional programming, currying typically uses a data-last approach.
It's nice, because it allows you to create functions from composed functions. So, yes, if you're piping a lot of functions into your pipeline, it could be annoying to have to find the end-of-the-line to check what data is actually being piped through. But, I'd argue that if you're piping that many functions through, you should consider making it into a named function anyway, so that readers don't have to parse through every step to understand what it's ultimately doing.
Manuel Romero already posted a slugify function (above) as an example, so I'll riff on that:
Great article Ben! When I moved to RxJS 6, I was always wondering why we need the
pipe
operator, this article really explains.smart and beautiful! kudos, Ben!
a little typo maybe in "Functional programming FTW!" paragraph:
function double(array) {
return arr.map(x => x + x);
}
I think it should be:
return array.map ...
;)
have a good week-end!!
Unfortunately typescript doesn't provide a good way to impose type constraints on functions with unlimited variadic arguments.
So pipeline function implementations like the above can be made type safe only by repeated overrides upto an arbitrarily determined maximum arity. Example.
I am curious why the RxJS team didn't opt for a fluent API (like this one) which does not suffer from a similar issue.
They originally did use a fluent API. Then for RxJS5... bad decisions were made.
Really good article. Very insightful!
But! Just to be a nitpicky jerk (and because it's oddly amusing to me to notice this):
Your three initial Array prototype extensions are
odds
,double
, andlog
. I don't know if there's a defined principle for this, but method naming should be consistent enough to allow for a certain amount of expectation to be met by the developer. My immediate assumption was thatlog()
would return an array of the log of each current array member, consistent withdouble()
(this is why I'm amused with myself, because that's such an unlikely method that has way less value). This speaks to the value of namespacing for the value of grouping methods by functionality. Maybe anArray.Math
object would have any math related extensions whileArray.Utils
could havelog()
. Or maybe I'm wrong?Obviously it's not central or relevant to your article, just noticed and thought it is its own interesting topic.
Fantastic! I wondered about this but I don't think I've ever actually understood it.
I've seen the pipeline operator a lot, though I haven't followed the proposal status. I'm not in love with the syntax but the functionality will be very much a gift!
I think meant for
function double(array) {
return arr.map(x => x + x);
}
to be
function double(array) {
return array.map(x => x + x);
}
Very understandable article! Also, great to see the pipeline operator proposal at the end :D
Thanks for the explanation on this, Ben!
I would like to implement this pipeline style programming but in real use cases, or better said, in more complex cases where we are used to face like asynchronous code, I have tried and I had to make use of some tricks to keep a nice composition, also debugging can be a headache (worse if you don't apply TDD).
Overall, nice post.
there is a pipeline operator draft too and a pip.this utility for using Array methods (or any other)
Reminds me that computer languages are converging on lisp and haskell. I've been using elm a lot lately and loving it.
If all you need/want is the
pipeWith
function, it can be written like this (I prefer to call it justpipe
):Thank you for the nice article, Ben.
I would also read a pipe vs lift vs let detailed article. Dont you plan to write one?
Phenomenal. Always wanted some nice clarity on the why behind piping.
We really need that pipeline operator to move to the next stage. It's been sitting there on stage 1 for a long time. Some activity lately, so hopefully in the next tc39 meeting they will make progress