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

Shorthand for piping a value through a bunch of functions #2809

Open
tfga opened this issue May 30, 2019 · 8 comments
Open

Shorthand for piping a value through a bunch of functions #2809

tfga opened this issue May 30, 2019 · 8 comments

Comments

@tfga
Copy link

tfga commented May 30, 2019

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.

@tfga
Copy link
Author

tfga commented Jul 16, 2019

Somebody implemented this as a babel macro:

https://github.com/Andarist/pipeline.macro

@jgonggrijp
Copy link
Collaborator

@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);

@tfga
Copy link
Author

tfga commented Apr 8, 2020

@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?

@jgonggrijp
Copy link
Collaborator

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!

@tfga
Copy link
Author

tfga commented Apr 9, 2020

@jgonggrijp Ok. Thanks for the replies.

@jgonggrijp
Copy link
Collaborator

@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.

@tfga
Copy link
Author

tfga commented Apr 10, 2020

@jgonggrijp No worries. Thank you for taking the time to explain this. I appreciate it.

@jgonggrijp jgonggrijp added the contrib probably belongs in Underscore-contrib label Aug 29, 2020
@jgonggrijp
Copy link
Collaborator

Reopening this, because I'm seriously considering to replace chain by pipe in future Underscore 2.0. Motivation:

  • chain only works with functions that have been added through mixin, while pipe can work with any function if implemented well.
  • chain prevents treeshaking (because of mixin), pipe doesn't.

Ditching chain might also imply ditching the OOP notation and fully committing to functional notation instead. Discussion welcome.

FYI @tfga.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants