Pipe Functions and Compose Functions | Javascript Functional Programming Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome hi i'm dave and today we're talking about functional programming specifically compose and pipe functions that are frequently used in functional programming now today's tutorial is an advanced javascript tutorial so you'll want to already be familiar with at least pure functions and curing functions and if you're not i'm going to put links to my tutorials for those in the description below okay let's get started with pipe and compose functions today we're talking about functional programming if you've heard of functional programming you know it works with pure functions and they're usually tiny small composable functions and composable is what we're talking about today is how do we link together these small functions well functional programming often uses what are called pipe and compose functions and those are higher order functions a higher order function is any function which takes a function as an argument and it returns a function or it does both which it takes it as an argument and returns a function let's look at how a compose function works i'm not going to make a compose function we will walk through essentially how it works first before we make one but what we need to start with are several small unary and that means one parameter functions and i usually say parameter i know the definition above here said argument but the word argument or arguments and parameter or parameters are often interchangeable okay so we're starting with three small functions we've got add two subtract one multiply by five they're unary functions they all just receive one parameter and from there we're going to execute these functions but notice how the functions execute from inside to outside and right to left you may have done this with a function before because add two is past the first parameter which is a number four and then it returns the result and the result is a number so we can just plug it in to the parameter for subtract one and subtract one is just plugged in as the parameter for multiply by five so notice how this executes from the inside to the outside and if this was nested if we were writing the functions out it would look like a staircase but we're going right to left instead of left to right so it starts with add two and it ends with multiply by five and then we can log the result to the console so let's take a look and we get 25 in the console and that's what we'd expect if you do the math from there let's note that the above is not a compose function i was just showing how a compose function would execute functions because it does go from right to left and it does essentially work its way from the inside to the outside so now let's make our own compose function before we make our own compose and pipe functions i'd like to note that the libraries ram to js and lodash both have their own built-in compose and pipe functions and note that lowdash calls pipe flow so let's look at how these work it uses the higher order function reduce and takes a list of values and applies a function to each of those values eventually accumulating a single result and if you think back to what we did with those three simple functions before that's essentially what we did we just didn't compile a list we accumulated the result so now let's write our compose function and to get the compose order from right to left in other words inside to out we need to use reduce right instead of reduce but you can see we set compose equal to a function and this function is curried if you remember what currying is and have watched my curing tutorial of course i'll give a link below in the description but it accepts a list of functions and then there is a value that is received so this is a function and this is a function and that's why it's curried and here on the functions we call reduce right and if you're familiar with reduce you'll know how reduce works but it works just as described above it takes a list of values and applies it a function to each of those values in this regard and therefore it accumulates a single result at the end i also have a tutorial on higher order functions and if those are new to you you should probably stop right now and learn more about higher order functions first i'll give a link below to that as well okay with our compose function defined we can call it and we can set comp result equal to compose and we pass in our three previous functions and notice we start with multiply by five on the left because the first function that will be called is add two it's going to start from the right and work its way left and then we're passing a parameter here at the very end so we're calling it into action immediately we're immediately invoking the function if you will and let's go ahead and log that to the console and when i save this we get 25 just as we did before but now we're using our compose function now what about pipe well there is something to do with that and that is if you don't like reading from right to left pipe essentially changes the order it doesn't use reduce right when we create our own it just uses reduce and so we can list the functions in the order from left to right that we want to use them in and that's why i like using pipe more than i like using a compose function once again low dash if you use the built in function in that library it is called flow and not pipe but these functions are typically referred to as compose and pipe or pipeline i have also seen so here we can set pipe result equal to add 2 on the left then subtract 1 then multiply by 5 and once again we'll immediately invoke our function and let's log the result and i'll save and since we passed in a 5 instead of a 4 the result is now 30 in the console you will also often see the functions on separate lines whether you're using a compose or a pipe function so now i'm setting this equal to pipe result 2 but it's essentially the exact same function and i'm once again providing a parameter immediately so it's invoked and now we're providing the parameter 6. let's go ahead and log this to the console and you can see the result is 35. the examples we've been looking at use a pointer free style and with a unary function you don't see the parameter passed between each function you just see the parameter added at the end of the compose or pipe function if we're call if we're immediately invoking the function it doesn't have to be that way and i'll show you an example of that as well but now let's look at some examples to where if you had possibly more than one parameter or you're not working with a unary function and so here's an example with that second parameter and i've got divide by but i don't say divide by two or divide by five so this function requires two parameters the divisor and then the number that's being divided and you can see it just returns the result so if we create a pipe here look at how we provide divide by in this instance that same pointer free x variable that is being passed between each of these functions which is the result so it would be the result of multiply by 5 here would be passed to x then we use an anonymous function and we call divide by and we just provide the number 2 here and then x will go into the function as well and we can go ahead and log this and that's how we would use a function with more than one parameter in our pipe or compose function but you could curry the divide by function to create a custom unary function because you're already hard coding the number two here so if you write divide by and i'll call it div by just so i don't have a duplicate definition then we curry the function and the first parameter it receives is the divisor the second parameter it receives is the number to be divided so it's essentially the same function but it's curried so it expects to receive two parameters at different times and this allows us to create a partially applied function so we can create a custom unary function called divide by two that's equal to our div by function that has already received the first parameter of two by doing that let's look at this pipe result here and you can see now we just put divide by two and it works as all the other unary functions once again i call it with five immediately afterwards so we're immediately invoking this and i'll go ahead and log this to the console let's look at some other simple examples that are not math functions i'm going to start out with a paragraph of lorem epsom and here we're just going to count the words in the paragraph so i've got my variable lorem right here and now i'm going to define a couple of functions i've got split on space and that's simply calling split on a string and it's looking for each space in the paragraphs or paragraph and then we're going to go ahead and count how many words we have after splitting the full string and that should give us a basic word count for the paragraph so if we were to create a pipe function and use it for word count first we would call split on space and then we would call count notice how i'm not immediately invoking the pipe function right now because you don't have to do that i did that in the previous examples but we can go ahead and console.log and call word count now because word count is a function and pass in lorem so let's save this word count essentially has our pipe function so we don't have to call it immediately or it's not immediately invoked unless we want to by passing the perimeter at the end and here you can see we get 60 words in our paragraph now the pipe function is also reusable so i've just got a variable here named eg bdf which stands for every good boy does fine if you took music courses you may have learned that sentence and now we can call word count on the eg bdf variable as well so i'll save and you can see we get a result of five in the console let's look at combining processes too so we're going to check for a palindrome which is a classic programming challenge and a palindrome is spelled the same forwards and backwards once you send everything to lower case and remove the spaces so you can see taco cat is definitely a palindrome we can just visually see this so is ufo tofu but my name dave that's not a palindrome so we're going to check each one of these but we need to combine processes to do so and we can make little functions along the way that will help us and then we'll pipe those functions together so we'll start out with these little functions and really i'm just taking some string methods and turning them into my own functions for this example so i've got split join lower and reverse and you can see how the reverse is an array method from there i can create a pipe to lower case and remove the spaces of my forward word that is going to be spelled the forward way so this would spell dave all lower case for example and now we can use that pipe again so let's look at the reverse pipe and notice i've nested in the forward pipe right into this reverse pipe so we have a nested pipe function and that's totally doable there's nothing wrong with that at all so now when we call the comparison and i'll log these to the console let's compare pal one as taco cat and pal 2 is ufo tofu and pal 3 is my name so we'll go ahead and save and look at the console we get true on the first two and faults on the last one our functions are working correctly and now you know you can use a nested pipe function in object oriented programming objects are often passed around and mutated by different functions unfortunately then you have to know the history of everything that has happened to that object sometimes to find a bug and that also makes it harder to test however in functional programming a state mutation or object mutation or just data mutation is really not allowed that's why we want pure functions and therefore you need to clone or copy objects and arrays and not mutate those objects so often times to solve this problem we need a clone function or a copy function and that could be within our pipe or compose functions that we're making so let's look at an example or two of that and we'll start with three different approaches actually so not just one or two but three here's the first one we can clone the object before an impure function mutates it and we'll you'll see what i mean by this so we'll start with a score object that just has home and away score and they're both zero and then we've got our shallow clone function now i do have a separate tutorial on making shallow copies and deep copies and you'll learn how to make those functions in that tutorial so i'll link to it below and it should pop up above right now as well so here's our shallow clone and it just checks to see if the object is an array and if it is it returns a new array so it's a shallow copy and it has copied in the previous array likewise if it's an object if it's not an array it should be an object and then it passes in a shallow copy of that object and that's what it will return so the next function in the pipe or compose functions that we're pulling together would be increment home and it expects an object so it should be receiving the clone or the copy if you will and not the original so it will mutate the object it receives right here setting object.home and incrementing it by one whatever the value is so in this case if it's zero it would be one when it's finished if we were to test this function by itself it's not pure because it does mutate the object but by putting it in a pipe with shallow clone in front of it it's going to receive a clone of the original object that we call the pipe function with and therefore this is a possibility let's go ahead and create that pipe and i'm calling it home score and you can see i'm calling shallow clone and then increment home and then we might have other functions that receive the object as well and let's go ahead and log the results here so we're going to call the home score pipe on the score object and then we'll look at the score object itself remember home score is going to return an object when it's finished so we'll see if those look alike and then we'll also use a strict comparison to see if those objects are identical or if we've created a new object and mutated the new object so when i save this we'll look to the console you can see after we call the home score function we get home one away zero but our original score object has not been mutated it's home zero away zero and so they are not equal now the positive of this approach is there are fewer function calls because we're only calling this clone function once and the entire chain of functions within our pipe but the negative is we have to create impure functions and that creates testing difficulties and possibly debugging difficulties and impure functions are not really what we want if possible in functional programming so this may not be the way to go the second approach is to curry the function which will create a partial that is unary for your pipe or compose function and so when we look at this we'll define our function increment just a little bit differently and of course i'll put a b behind it just so it's a separate function and i'm also using the keyword let which is important and it's with this curried function i'm passing in the clone function as the first parameter and you can tell it's curried because here's a function call and here's a function call and then we pass in the object as the second parameter inside the function now we create a new object and we call the clone function that is passed in and here is still mutation on the new object but that's okay because it's happening to the new object and not the original object that we passed in and then we return the new object and we're not quite finished after that definition then this is why we use the keyword let then we need to take our increment home b and set it equal to increment home b and pass in that clone function so this is making a partial through curing and after that we can go ahead and create our pipe and we just have to pretend there's some other functions in the pipe or if we wanted to use compose either way and this function will work here now as a unary function and so we can call our home score b with the score object and we'll also just pass the score object to the console once again so we can compare and i'll save and we get home 1 away 0 and still the score object is unmutated the pure function with clear dependencies created through curing makes this a great choice and out of the three approaches this may be my favorite but the negative is there's more calls to the cloning function because if you need to do something else with an object and something else with an object and you're creating all of these that may mutate objects or arrays then you need to pass in that clone function to each one of those however this does meet the definition of a pure function and it does allow for easier debugging and testing and that's what functional programming is all about and the third and final approach is a lot like the second but we do not curry the function we do insert the clone function as a dependency though so let's take a look at this function and it looks more like we would expect from a normal function where it just receives two parameters the object and the clone function and then inside the body of the function looks much like the curried function basically identical here for these three lines from there we can create our home score c with a pipe and you can see now how we call this increment home c function and we're just passing in x there say possibly a function before it whatever the unary function returns is going to be x and it should be an object or an array here and then we explicitly pass in our shallow cone clone function right here inside of the pipe and so this would be another way to handle this without creating a partial because you can do this inside of a pipe or compose function so from here let's go ahead and call home score c and pass in the score object and compare the original score object again and if we look at the console once again we get our new object that has home one away zero and the original is still unmutated so the positive aspects of this are we get a pure function with clear dependencies and the negative part is non-unary functions in your pipe and composed chain and they just don't look as good and they're a little harder to work with but it's not a huge negative so if you wanted to go this route that would be fine i kind of leaned towards the second one and that was the curried function that creates a partial and then it's unary inside the pipe or compose just like the other functions are so i hope you now know how to create your own pipe and compose functions and you're curious about functional programming because even if you don't strictly adhere to it following some of the functional programming principles can keep your code clean and dry of course that means don't repeat yourself hey thank you guys for watching and subscribing i sincerely appreciate it and thank you for helping my channel grow i'll see you guys next time
Info
Channel: Dave Gray
Views: 22,446
Rating: undefined out of 5
Keywords: Pipe Functions and Compose Functions, pipe functions, compose functions, pipe, compose, javascript, functional programming, functional programming tutorial, javascript functional programming tutorial, pipeline functions, pipelining functions, function composition, functional programming in javascript, programming, functional, function, functions, pure functions, pure function, reduce, higher order functions, higher order function, composable functions, reduce function, js
Id: kclGXphtmVg
Channel Id: undefined
Length: 21min 51sec (1311 seconds)
Published: Fri Jun 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.