underscore
underscore copied to clipboard
Shorthand for piping a value through a bunch of functions
Let's say you want to pipe some value a through 3 functions f1, f2, f3, in that order. How would you do it?
Well, you can do it the "normal" way (pure JS):
f3(f2(f1(a)))
Or you can use _.compose():
_.compose( f3
, f2
, f1
)(a)
But what I'd really like to write is:
_.pipe( a
, f1
, f2
, f3
)
Both JS's function composition syntax and _.compose() make me write the function list in the reverse order. Clojure has threading macros; in Elm, you could write this using the reverse apply operator:
a
|> f1
|> f2
|> f3
This _.pipe() function I'm suggesting would be a way of doing something similar in JS.
Somebody implemented this as a babel macro:
https://github.com/Andarist/pipeline.macro
@tfga You can easily implement this yourself and then add it to Underscore if you want:
var pipe = _.restArguments(function(input, functions) {
return _.compose.apply(null, functions.reverse())(input);
});
_.mixin({pipe: pipe});
_.pipe(a, f1, f2, f3);
Or with ES6 syntax:
function pipe(input, ...functions) {
functions.reverse();
return _.compose(...functions)(input);
}
_.mixin({pipe});
_.pipe(a, f1, f2, f3);
@jgonggrijp But then I'd have to duplicate this code in every project. To prevent that, isn't it the point of libraries like underscore?
No, you don't have to copy it in every project. You can make your own NPM package that imports Underscore, adds your desired functions to it with _.mixin, and then exports the result as a "super Underscore". If you use that package everywhere instead of Underscore itself, no code gets duplicated and you get all original functions of Underscore unmodified as well as your own additions. Win-win!
@jgonggrijp Ok. Thanks for the replies.
@tfga I guess I should have started by mentioning why I think this doesn't belong in Underscore. Sorry for not doing that.
I see the added value of _.pipe. It also combines well with chaining:
_.chain(a).pipe(f1, f2, f3).pipe(f4, f5).value();
But the problem is, there is an infinite space of functions that are not in Underscore but that would have an added value. We can't add all of them. In fact, we have to be conservative because Underscore is trying not to grow fatter (see #2060).
I'm not ruling out that new functions would be added (in fact that's not up to me to decide), but given that pipe is just reverse + compose, to me it doesn't seem like a game-changing addition.
Another thing I failed to mention is that there is a very similar function available from Underscore-Contrib: pipeline.
@jgonggrijp No worries. Thank you for taking the time to explain this. I appreciate it.
Reopening this, because I'm seriously considering to replace chain by pipe in future Underscore 2.0. Motivation:
chainonly works with functions that have been added throughmixin, whilepipecan work with any function if implemented well.chainprevents treeshaking (because ofmixin),pipedoesn't.
Ditching chain might also imply ditching the OOP notation and fully committing to functional notation instead. Discussion welcome.
FYI @tfga.