Implementing a Pipe Function Exercise

Lesson Description
The "Implementing a Pipe Function Exercise" Lesson is part of the full, Functional JavaScript First Steps, v2 course featured in this preview video. Here's what you'd learn in this lesson:
Students are instructed to create a pipe function that takes an array of functions as input and calls the functions in order.
Transcript from the "Implementing a Pipe Function Exercise" Lesson
[00:00:00]
>> Anjana Vakil: Our next task is going to be to write a function called pipe, which is a common functional programming utility. That we could import from a different library, but we're not gonna, we're gonna make it. That is going to take a bunch of different functions, however many I want, and it's going to compose them together
>> Anjana Vakil: Into a single function where I don't have to nest the calls in a backwards order.
[00:00:41]
So basically, this utility is going to allow me to write code left to right that says stuff like, okay, first join these values together, and then split up that return value, and then reverse that. For example, these are just simple string manipulations, or array manipulations. And this is gonna help us not have to do that mental gymnastics of reversing the order of operations in our heads every time.
[00:01:08]
So this is a very handy utility in the functional program or toolbox, is a pipe or pipeline function that takes a bunch of functions in as arguments and returns the composition in the order you would expect. So that is started out for you here in funpro.js, a new addition to our toolkit here.
[00:01:37]
We still have filter, we have map, we have reduce, we have all of our little helpers. And what we're gonna do is use all of the tools we have accumulated so far to implement this pipe function so that we don't have to keep doing that, and exclaim, and then announce, and then [INAUDIBLE].
[00:01:57]
So that we can just write code more like we would express it verbally. It's gonna need recursion. There's some notes in here about what you're gonna wanna do.
>> Anjana Vakil: Let's have at it.
>> Anjana Vakil: Let's make a pipeliner. [LAUGH] So, I added a few hints in the comments, and in this little purple box thingy.
[00:02:30]
Again, we are recursing all the way down, and we are using those little array helpers just to practice the functional principles. And there's one other thing that might become useful for this, which is the spread of an array. So the thing is, the pipe function is not taking in an array, it's taking in a bunch of comma-separated arguments, each of which is a function.
[00:02:59]
So we don't know how many, pipe should work with any number of functions. It should be able to take in as many arguments as we want, and so the spread operator is a handy way of taking as many arguments as pipe received and capturing them as this funds array.
[00:03:17]
And then we can also use spread to expand an array if we need to. And if you haven't worked with spread operators much, totally chill, there's also other ways to do this. And if you're curious about them, MDN them. All right, base cases, good place to start. So, zero functions, our pipeline is done.
[00:03:40]
Which do we return, however? Pipe always needs to return a function. So in this case, we can return what I've called the identity function, but basically, a no op function, a function that does nothing [LAUGH] to its input. It still has to be a function, though. And this is gonna be in our base case where, if we have no functions, so if our little length helper of fns, and fns here is an array of all of the function arguments that were passed in to pipe.
[00:04:15]
If the length is 0, then we will return a function that essentially takes in whatever input we give it and spits it right back out. Does nothing. But we need a base case, so, good starting point. Sound okay? All right, second base case. If there's one function, so, if (length(fns) == 1), then what do we want to return?
[00:04:48]
Well, we wanna return a function. So I'm just gonna put an arrow here to remind us to do that. And what?
>> Anjana Vakil: Well, there's a couple different ways we could write this that don't all involve that. What would we like to return here?
>> Anjana Vakil: So, if I give it an input, this returned function, the output that I want is?
[00:05:11]
>> Speaker 2: The first function of input, the head.
>> Speaker 3: Calling that function with that input.
>> Anjana Vakil: Passing that input to this function. So one way I could write this, to be super explicit, could be that we take the head of fns, and then, and this is where we're starting to get into more functioning syntax.
[00:05:31]
Head of fns is a fun, [LAUGH] it's a function. So that means I can open some parens to call it on the input, okay? But there's an even shorter way I could have written this, which is that I could just return the function, cuz that's already what this function does.
[00:05:52]
It takes an input and it does its operation on the input, right? So either way, totally legit, equivalent. All right, base cases sorted, now all we need to do is draw the rest of the recursion. So, as I've mentioned in the comment right here, if we have multiple functions, we're going to apply the first one, or call it,
>> Anjana Vakil: And then,
>> Anjana Vakil: Pipeline it through another pipeline that's all the rest of the function.
[00:06:35]
Let's put that into code. There's gonna be some parentheses, but we will get through it. All right, so I'm still gonna return a function, and that function is gonna take in whatever input, just like the other cases. We're always returning a function that takes input and returns output, right?
[00:06:53]
So we're taking in this function that returns something, and is going to involve a call to pipe. [LAUGH] All right, now we said, we wanna create a recursive pipeline of the rest of the functions. Can anybody help me tell JavaScript to do that?
>> Speaker 4: We need a tail.
[00:07:17]
>> Anjana Vakil: Tail, right? So the counterpart to head, we're gonna work with the tail of the fns. And then the thing is, the tail method or function-
>> Speaker 5: Turns an array.
>> Anjana Vakil: It's actually an array when it comes back. So this is gonna be an array of functions.
>> Speaker 6: So we gotta spread it.
[00:07:38]
>> Anjana Vakil: But pipe doesn't take an array of functions, so one thing we can do, and this is where that spread operator is helpful. We can take whatever the things are in this array and spread them out, sort of unwrap them from the array and pass them in as arguments.
[00:07:51]
So this would be sort of equivalent to passing in each of the elements in the array with commas between them.
>> Anjana Vakil: However, we are not done, because this neglects our good friend, the head function, the first function.
>> Anjana Vakil: And this is where the mental gymnastics start happening. So, pipeline returns a function.
[00:08:22]
>> Anjana Vakil: And that function,
>> Anjana Vakil: If we go back to our
>> Anjana Vakil: Graphic here, right?
>> Anjana Vakil: Which do we want to apply first? The f function or the g function? So, in our case, the head, or all the rest of the pipeline? But whatever input we're passing to this pipeline, the first function we wanna pass it through is the first function passed in as an argument to pipe.
[00:08:59]
So that means, if we're listing these in, we're writing this for ourselves, to be able to stop thinking about what order these functions are executed in. We wanna be able to pass in f and then g and still get out the correct application. So in this case,
>> Anjana Vakil: The first thing we wanna do is, similarly to before, we wanna take the head function and call it on the input, right?
[00:09:28]
And so before we had this head input signature, like that, we're gonna do that. And that, if I just say,
>> Anjana Vakil: We don't have to do this with a variable. But if I say pipe first is the head of the funds applied to the input, which doesn't make sense cuz we're not in the body of the function here, but you know what I mean.
[00:09:54]
Imagine we had curly braces, okay? And we want to take that. That is essentially, our green triangle here. That's the first output. And we wanna take that and pass it into the rest of the pipeline. Help me get my parentheses in the right places, please. Where should I put this little snippet of code right here?
[00:10:16]
This head(fns)(input). Now, that's the first thing that I want to happen in the execution, which means it's probably gonna be- The last. Backwards in our calling of it. So this is gonna be that first output. And we wanna pass that in to the rest of the pipeline, which means we need more, close paren, open paren, and then the output value, whatever the result is of calling the first function in fns.
[00:11:02]
On whatever input we had piped in, that whole thing is essentially like the first function output, and that is then gonna be the input that we pass to our recursive pipeline. Let's take a look at what's happening in our tests. We now have a way of left to right piping a bunch of different functions, composing them together so that we don't have to think about the order of operations.
[00:11:34]
And so there's, the last test in this test suite is double checking that we are doing that. So for example, here I have this super useful function. We We have a bunch of little functions that are all doing one thing. So we have a reverse, that reverses an array.
[00:11:50]
We have a join, that concatenates all of the values in array into a single string. And then we have a split, that splits a string into individual characters. And we are double checking that our pipeline is applying the functions in the correct, intended order, by making sure that we're getting different results out, okay?
[00:12:11]
So here what we're doing, is if we're reversing and then joining and then splitting, we're taking the array, pipes are cool, reversing it into the array, cool are pipes. Joining that all together into, cooler pipes of string, and then splitting that up into individual characters, to get cooler pipes in an array.
[00:12:33]
And that is not the same thing as if we took that exact same input and put it through a differently ordered pipeline, where here, first we're smooshing particle into one string. And then we are splitting it up into a bunch of, that's our cool letters, and then we are into an array, and then we are reversing that array and getting, look at us poop.
[00:12:58]
[LAUGH] So, this is just a test to again, prove to ourselves that we have now sort of hoisted ourselves by our own functional bootstraps into being able to use a more human readable. But still purely functional way of chaining inputs through pipelines of multiple functions. Well, rather, of creating that pipeline, which itself, is a single function.
[00:13:26]
So now we've got this pipeline, it's like this one big black box that we don't have to do any mental gymnastics to call the functions, nest them in the correct way. And now we have a function that takes in our original input and performs all of the transformations we want, and gets our ultimate output.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops