Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for JS Functional Concepts: Pipe and Compose
JoelBonetR 🥇
JoelBonetR 🥇

Posted on • Edited on

     

JS Functional Concepts: Pipe and Compose

Function piping and composition are concepts from functional programming that of course are possible in JavaScript -as it's a multi-paradigm programming language-, let's deep into this concepts quickly.

The concept is to execute more than a single function, in a given order and pass the result of a function to the next one.

You can do it ugly like that:

function1(function2(function3(initialArg)))
Enter fullscreen modeExit fullscreen mode

Or using function composition

compose(function3,function2,function1)(initialArg);
Enter fullscreen modeExit fullscreen mode

or function piping

pipe(function1,function2,function3)(initialArg);
Enter fullscreen modeExit fullscreen mode

To make it short,composition and piping are almost the same, the onlydifference being the execution order; If the functions are executed from left to right, it's a pipe, on the other hand, if the functions are executed from right to left it's called compose.

A more accurate definition would be: "In Functional Programming, Compose is the mechanism that composes the smaller units (our functions) into something more complex (you guessed it, another function)".

Here's an example of a pipe function:

constpipe=(...functions)=>(value)=>{returnfunctions.reduce((currentValue,currentFunction)=>{returncurrentFunction(currentValue);},value);};
Enter fullscreen modeExit fullscreen mode

Let's add some insights into this:

Basics

  • We need to gather a N number of functions
  • Also pick an argument
  • Execute them in chain passing the argument received to the first function that will be executed
  • Call the next function, adding as argument the result of the first function.
  • Continue doing the same for each function in the array.
/* destructuring to unpack our array of functions into functions */constpipe=(...functions)=>/* value is the received argument */(value)=>{/* reduce helps by doing the iteration over all functions stacking the result */returnfunctions.reduce((currentValue,currentFunction)=>{/* we return the current function, sending the current value to it */returncurrentFunction(currentValue);},value);};
Enter fullscreen modeExit fullscreen mode

We already know that arrow functions don't need brackets nor return tag if they are returning a single statement, so we can spare on keyboard clicks by writing it like that:

constpipe=(...functions)=>(input)=>functions.reduce((chain,func)=>func(chain),input);
Enter fullscreen modeExit fullscreen mode

How to use

constpipe=(...fns)=>(input)=>fns.reduce((chain,func)=>func(chain),input);constsum=(...args)=>args.flat(1).reduce((x,y)=>x+y);constsquare=(val)=>val*val;pipe(sum,square)([3,5]);// 64
Enter fullscreen modeExit fullscreen mode

Remember that the first function is the one at the left (Pipe) so 3+5 = 8 and 8 squared is 64. Our test went well, everything seems to work fine, but what about having to chainasync functions?

Pipe on Async functions

One use-case I had on that is to have a middleware to handle requests between the client and a gateway, the process was always the same (do the request, error handling, pick the data inside the response, process the response to cook some data and so on and so forth), so having it looking like that was a charm:

exportdefaultasyncfunctionhandler(req,res){switch(req.method){case'GET':returnpipeAsync(provide,parseData,answer)(req.headers);/*        ...      */
Enter fullscreen modeExit fullscreen mode

Let's see how to handle async function piping in both Javascript and Typescript:

JS Version

exportconstpipeAsync=(...fns)=>(input)=>fns.reduce((chain,func)=>chain.then(func),Promise.resolve(input));
Enter fullscreen modeExit fullscreen mode

JSDoc Types added to make it more understandable (I guess)

/** * Applies Function piping to an array of async Functions. * @param  {Promise<Function>[]} fns * @returns {Function} */exportconstpipeAsync=(...fns)=>(/** @type {any} */input)=>fns.reduce((/** @type {Promise<Function>} */chain,/** @type {Function | Promise<Function> | any} */func)=>chain.then(func),Promise.resolve(input));
Enter fullscreen modeExit fullscreen mode

TS Version

exportconstpipeAsync:any=(...fns:Promise<Function>[])=>(input:any)=>fns.reduce((chain:Promise<Function>,func:Function|Promise<Function>|any)=>chain.then(func),Promise.resolve(input));
Enter fullscreen modeExit fullscreen mode

This way it will work both for async and non-async functions so it's a winner over the example above.

You may be wondering what aboutfunction composition, so let's take a gander:

Function Composition

If you prefer to call the functions from right to left instead, you just need to changereduce forredureRight and you're good to go. Let's see the async way with function composition:

exportconstcomposeAsync=(...fns)=>(input)=>fns.reduceRight((chain,func)=>chain.then(func),Promise.resolve(input));
Enter fullscreen modeExit fullscreen mode

Back to the example above, let's replicate the same but with composition:

How to use

constcompose=(...fns)=>(input)=>fns.reduceRight((chain,func)=>func(chain),input);constsum=(...args)=>args.flat(1).reduce((x,y)=>x+y);constsquare=(val)=>val*val;compose(square,sum)([3,5]);// 64
Enter fullscreen modeExit fullscreen mode

Note that we reversed the function order to keep it consistent with the example at the top of the post.

Now, sum (which is at the rightmost position) will be called first, hence 3+5=8 and then 8 squared is 64.


If you have any question or suggestion please comment down below

Top comments(48)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
joshuakb2 profile image
Joshua Baker
  • Joined

I'd like to point out that the pipeAsync and composeAsync functions are examples of monadic composition! 😁

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

Indeed they are! 👌🏼😁

CollapseExpand
 
wiseai profile image
Mahmoud Harmouch
Stay humble like a bumblebee 🐝.
  • Email
  • Location
    Ferris Cosmos 🌌
  • Education
    Diploma in Rust
  • Pronouns
    he/him
  • Work
    Freelance Rust Engineer
  • Joined

Thanks for sharing such a neat concept. Also, +1 on using JSDoc (Get the TS code outta my face. Kidding, keep it.).

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

😂

CollapseExpand
 
wiseai profile image
Mahmoud Harmouch
Stay humble like a bumblebee 🐝.
  • Email
  • Location
    Ferris Cosmos 🌌
  • Education
    Diploma in Rust
  • Pronouns
    he/him
  • Work
    Freelance Rust Engineer
  • Joined
exportconstpipeAsync:any=(...fns:Promise<Function>[])=>(input:any)=>fns.reduce((chain:Promise<Function>,func:Function|Promise<Function>|any)=>chain.then(func),Promise.resolve(input));
Enter fullscreen modeExit fullscreen mode

types aren't that useful

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined
• Edited on• Edited

To be fair in this situation it can, effectively, beany 😅
The return type would be the return type of the last function called and the type to pass to the next function would be the output type of the one before 🤯 just in case you are up for writing a type function 😁

Thread Thread
 
wiseai profile image
Mahmoud Harmouch
Stay humble like a bumblebee 🐝.
  • Email
  • Location
    Ferris Cosmos 🌌
  • Education
    Diploma in Rust
  • Pronouns
    he/him
  • Work
    Freelance Rust Engineer
  • Joined

These situations always keep me thinking about why I am using TS in the first place. You will end up spending so much time figuring out the types in these tricky situations, which is NOT a business problem to work on in the first place. That's why I would love to revert back to JS, anytime.

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

Because the absence of types (norTS norJSDoc+TS Pragma ) leads sooner or later to non-expected paths that can break the app in runtime, which is a business problem that will rain as sh*t over the dev team (Imagine you spent 200k on a marketing campaign and the app crashes avoiding the conversion that could eventually amortize the costs plus benefits).

Are the types necessary in every single App? No, they aren't.

Still it's recommended to have them in most apps. On the other hand, JSDoc + TS Pragma (remember that JSDoc alone does nothing but printing an informative text, you need TS Pragma to get type checks on dev time) is better than nothing, but TS has much more features than that.

Thread Thread
 
wiseai profile image
Mahmoud Harmouch
Stay humble like a bumblebee 🐝.
  • Email
  • Location
    Ferris Cosmos 🌌
  • Education
    Diploma in Rust
  • Pronouns
    he/him
  • Work
    Freelance Rust Engineer
  • Joined

Because the absence of types (nor TS nor JSDoc+TS Pragma ) leads sooner or later to non-expected paths that can break the app in runtime, which is a business problem that will rain as sh*t over the dev team

Yep. Although the absence of types may not be considered a major problem by some, me included, I believe that not following best practices is what ultimately causes more issues in web development. By adhering to established conventions and standards, we can avoid many of the potential problems that can occur without these guidelines. In addition, following best practices often leads to code that is easier to read and understand, which can save time and frustration for all parties involved.

But, the thing is that major business issues, even though I don't have data but from what I have experienced, are not necessarily caused by the absence of types. I think there is a correlation between the two. But, the relation is not causation. The absence of types doesn't necessarily lead to rain as sh*t over the dev team. Know what I am saying?

Are the types necessary in every single App? No, they aren't.

Agree.

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined
• Edited on• Edited

Of course not, the main issue is not having tests.
Key in the discussion here being that coding inTypeScript is faster thanvanilla JS + JSDoc + TS Pragma, you may never seen it this way but look:

/** * Sums two numbers * @param  {number} n1 * @param  {number} n2 * @returns {number} */constsumTwoNumbers=(n1,n2)=>n1+n2;
Enter fullscreen modeExit fullscreen mode
/** Sums two numbers */constsumTwoNumbers=(n1:number,n2:number)=>n1+n2;
Enter fullscreen modeExit fullscreen mode

As well as more reliable.
To get a similar reliability with JSDoc you need to ensure JSDoc is added and maintained through automatisms in the linter and run this step in the PR's pipeline, at least (i.e. ESLint plugin JSDoc) and it takes more to configure than what it takes to configure TS most of the time.

Keep in mind that using JSDoc and TSPragma you are just using one little piece of TS, which is about type definition and type reports (and it doesn't even cover it entirely).

If you just need those features, then add TS and just use those features 😂

Do you dislike interfaces? Fine, don't use them, if you are working in FP instead OOP, interfaces doesn't even make sense (in FP all functions are interfaces).

It is not mandatory to use everything from TS so don't stress it so hard 😁

Thread Thread
 
wiseai profile image
Mahmoud Harmouch
Stay humble like a bumblebee 🐝.
  • Email
  • Location
    Ferris Cosmos 🌌
  • Education
    Diploma in Rust
  • Pronouns
    he/him
  • Work
    Freelance Rust Engineer
  • Joined

💯

CollapseExpand
 
mihailtd profile image
Mihai Farcas
I am a Software Architect passionate about building impactful healthcare solutions. Experienced with k8s, Node.js, Vue.js, MongoDB, and cloud technologies. Check out my work at https://mihai.ltd!
  • Email
  • Location
    Cluj-Napoca, Romania
  • Work
    Software Architect @ Marsh McLennan
  • Joined

Great article! Love the fact that you include theasync versions.
Glad to see people interested in this topic!
I have a YT video on this same topic:youtube.com/watch?v=q1aNVIq3K7c
where I show some real world examples. Feedback is very appreciated!

CollapseExpand
 
apotre profile image
Mwenedata Apotre
Passionate Backend developer , AI Engineer & Devops Enthusiast , always keen to learn and explore new things, pursued Software Engineering and Cyber Security.
  • Email
  • Location
    Kigali, kigali, Rwanda
  • Education
    Rwanda Coding Academy
  • Pronouns
    He/Him
  • Work
    FullStack developer(Backend Heavy)
  • Joined

I just knew pipe and now I know it differs to compose by just order of function execution! Thanks

CollapseExpand
 
dmass profile image
Douglas Massolari
Frontend developer. Neovim ❤️
  • Location
    Toulouse, France
  • Work
    Functional Frontend at Tweag
  • Joined
• Edited on• Edited

That's not always true, though.
In Elm, for example, pipe|> and composition>> have the same order of execution, the difference is that pipe is imediately executed while composition returns a new function:
Note: Everything after-- is a comment in Elm

addxy=x+ysubyx=x-ytotalWithPipe=1|>add10|>sub5-- 6totalWithComposition=letcalculateTotal=add10>>sub5-- returns a functionincalculateTotal1-- 6
Enter fullscreen modeExit fullscreen mode

Not only in Elm, butpipe in fp-ts also works this way:

import{pipe}from'fp-ts/function'constlen=(s:string):number=>s.lengthconstdouble=(n:number):number=>n*2// without pipeassert.strictEqual(double(len('aaa')),6)// with pipeassert.strictEqual(pipe('aaa',len,double),6)
Enter fullscreen modeExit fullscreen mode
CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

That's why the post has the tag #javascript 😁, either way love the insight! I haven't coded in Elm in ages, actually a good one, absolutely love the no runtime errors 🤩

It's sad that it got relatively few support...

Thread Thread
 
dmass profile image
Douglas Massolari
Frontend developer. Neovim ❤️
  • Location
    Toulouse, France
  • Work
    Functional Frontend at Tweag
  • Joined

Yes, but even in Javascript this concept can be different as you can see in fp-ts’ pipe.

I love coding in Elm! It is my first option when creating a Frontend.

From what I see, it seems some companies are adopting it, so, it seems it’s growing

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined
• Edited on• Edited

I picked the mathematical explanation for function composition:

In abstract algebra, a composite function is a function formed by the composition or successive application of more than one function. To do this, the function closest to the argument is applied to the argument, and the next function is applied to the result of the previous calculation.

in which case, this will fit in the description:

compose(function3,function2,function1)(initialArg);
Enter fullscreen modeExit fullscreen mode

The implementation details or nuances in Elm (or any other) is a different matter of discussion 😁

BTW glad to hear Elm it's getting a bit more love!

Thread Thread
 
dmass profile image
Douglas Massolari
Frontend developer. Neovim ❤️
  • Location
    Toulouse, France
  • Work
    Functional Frontend at Tweag
  • Joined

You are right.
But the point of my comment ispipe.
This is the one that have different implementations.
I just highlighted that the affirmation “pipe is the same as composition but reversed” is not always true

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

oh! understood now 😁 my bad

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

Anytime 😁

CollapseExpand
 
edlinkiii profile image
Ed Link III
{ WebDeveloper: [ HTML, CSS, Vanilla JS, jQuery, PHP, C#/.Net, SQL ],Personal: [ Husband, Autism & Epilepsy Dad, Drummer, Irreligious Theology Nerd, Sci-Fi Geek, ADHD, ISFJ, Gemini, Xennial ] }
  • Location
    Ohio, USA
  • Education
    Self Taught: Books, Online, Trial & Error...
  • Work
    Web Developer
  • Joined

IMHO, the "ugly way" is a lot more intuitive and easier to read. Not that I don't appreciate the work you've done or the insight I gained from reading your article. ✌🏻

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined
• Edited on• Edited

It's your opinion and when you use it in your projects, it will be your code, so use the style you prefer or feel more comfortable with, it's totally OK! 😁

Edit: I wrote the post in different days (one bit at a time) and just realized I had been using different wording for the references on those functions

So for this one:

constpipe=(...functions)=>(initialArg)=>{returnfunctions.reduce((currentValue,currentFunction)=>{returncurrentFunction(currentValue);},initialArg);};
Enter fullscreen modeExit fullscreen mode

we could save few keyboard clicks by coding it like this:

constpipe=(...functions)=>(initialArg)=>functions.reduce((currentValue,currentFunction)=>{returncurrentFunction(currentValue);},initialArg);
Enter fullscreen modeExit fullscreen mode

Just like this in the last one:

exportconstpipeAsync:any=(...fns:Promise<Function>[])=>(initialArg:any)=>fns.reduce((currentFunc:Promise<Function>,func:Function|Promise<Function>|any)=>currentFunc.then(func),Promise.resolve(initialArg));
Enter fullscreen modeExit fullscreen mode

Which is probably more... understandable?

Let me know, if it helps I can update the post! 😁

CollapseExpand
 
gohomewho profile image
Gohomewho
I am learning to learn.
  • Location
    Taiwan
  • Work
    Frontend developer
  • Joined

WoW this is so cool!

In the last example, I think thepipe frompipe(square, sum)([3, 5]); should becompose.

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

Hi Gohomewho, thanks for pointing it out! I'm fixing it right now 🙂

CollapseExpand
 
jwp profile image
JWP
WebCompnents and AI
  • Location
    Minneapolis, MN
  • Joined

Nice Joel, thanks 😊

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

Thank you for reading John! 😉

CollapseExpand
 
alessioferrine profile image
alessioferrine
  • Joined

It's quite interesting things, thanks for writing about it

CollapseExpand
 
annabaker profile image
Anna Baker
Hi, I'm Anna Baker and I'm an online marketer and web security junior. I have a growing experience in the industry and have a deep understanding of both front-end and API security.
  • Joined

Great Post!

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

Thank you Anna! 🙂

CollapseExpand
 
vinsay11 profile image
Winsay vasva
Helping Others Makes me Happy; Making them Smile is the Art.
  • Education
    MBA
  • Work
    Manager
  • Joined

good article

CollapseExpand
 
joelbonetr profile image
JoelBonetR 🥇
Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

Thank you Winsay 😁

Some comments have been hidden by the post's author -find out more

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Tech Lead/Team Lead. Senior WebDev.Intermediate Grade on Computer Systems-High Grade on Web Application Development-MBA (+Marketing+HHRR).Studied a bit of law, economics and design
  • Location
    Spain
  • Education
    Higher Level Education Certificate on Web Application Development
  • Work
    Tech Lead/Lead Dev
  • Joined

More fromJoelBonetR 🥇

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp