This course has been updated! We now recommend you take the Functional-Light JavaScript, v3 course.
Transcript from the "Challenge 8: Solution" Lesson
[00:00:00]
>> Kyle: Let's talk through the solution for exercise 8. I'm gonna start first by implementing the filterobj and reduceobj functions. So as my starting point I'm just gonna copy and paste that implementation and then edit from there. So, I am not gonna be calling a mapperFn here. I'm just gonna be calling that but I am going to have a conditional.
[00:00:27] So I am going to say if the predicateFn and what I need to pass into it is o of key. So I don't set it on the new object if it doesn't pass the predicate check. It's just not added to the new object. And then for,
>> Kyle: The reduced object function, we need to have a kind of that running total, if you will.
[00:01:02] And I'm gonna do that by just making a result variable that starts out = to initialValue. And again, I'm not calling a mapper function, but I am gonna be calling a,
>> Kyle: So I'm gonna say result =, and I'm gonna be calling the reducer function, and I need to pass along the current running result, as well as the new value here.
[00:01:36] And now we realize wait a minute, I'm not actually making a new object. Reduce doesn't make a new object. So let's get rid of the new object and the new object. And instead, what do we want to return?
>> Speaker 2: Result.
>> Kyle: Result.
>> Kyle: Okay, any questions about that part, implementing either of those two?
[00:02:06]
>> Kyle: It's pretty straight forward to implement those ideas against the data structure like the object. The more sophisticated you get, the more complex it might get. So for example, when I implemented filter against a binary tree. When you start removing nodes from a tree, you actually need to rebalance the tree.
[00:02:25] And so there was 30 or 40 lines worth of code that was just rebalancing the tree, had nothing to do with the filter algorithm. But that data structure in particular, had a lot of extra work to implement, so. But the filter reduce in that parts are pretty straight forward.
[00:02:42] Okay, let's come up here, and talk about somethings that we could do differently with this code. First off, I have three different statements that represent a flow of data. My flow of data is starting out with my nums. That becomes filtered nums which then goes in here and becomes filtered num products.
[00:03:04] Which then goes in here and becomes this number. So there's a flow of data through these operations from an object to final and result number. What does a flow of data generally look like to you?
>> Kyle: Through a set of functions. Should look like a compose or actually I'm gonna represent this with pipe instead of with compose.
[00:03:29] So just to sketch out my ideas here in terms of how I am gonna implement. I am go do a pipe here, and that's gonna create me a function that I need to pass something in. What am I passing in to my piped function?
>> Kyle: I'm passing in the nums, right?
[00:03:50] That's the input value, it's gonna come into the first function. So I'm basically gonna have a function 1, a function 2, and a function 3 here. Nums is going to come into function 1 and get changed into something. That's going to become the input to function 2, which is then going to output to function3, which is then going to output my final number value.
[00:04:10] So let's start at the bottom, let's start with the reduce object, because I've been past an object and I want to reduce it to a number. This reduce object takes three inputs. It takes a reducer, an initial value, and it takes a input value. I wanna provide two of those three right now.
[00:04:37] The third one is going to come from the output of functiont 2. So what do you think I could do to provide three of those right now? I mean, two of the three right now. Could do a partial application. I'm gonna do it with currying, since we provided you the curry utility.
[00:04:53] So to replace function three in my little piping call, I'm gonna curry and this is the function that I'm currying, which is reduce object. That's my utility. The first input that I need to provide is this reducer.
>> Kyle: The next input that I need to provide is this initial value.
[00:05:24] So it's waiting on the last input, which is gonna take that place. I don't need to worry about passing that one in. That's what the pipe is gonna do for me. Have we follow that recently?
>> Kyle: Now, let's look at this function that I just copied in. What we notice about the shape and the behavior of that function?
[00:05:48]
>> Kyle: Does it look like anything else that we already have provided to us? It looks like the sum utility doesn't it? So why don't we just replace that thing with the sum utility?
>> Kyle: And that has gotten rid of one of those three operations. So let's keep working our way backward.
[00:06:12] We need to call the map object with two inputs. Well, that sounds like another curry to me. So I'm gonna curry the map object function. And I need to provide it this utility.
>> Kyle: Now let's look at that utility for just a moment. What does that look like to you?
[00:06:50]
>> Speaker 3: List product.
>> Kyle: It looks like list product, right? That's a point that we don't need ,so why don't we just use list product as the napper function.
>> Kyle: Everybody following how I'm doing this? One last one to do. You'll notice that I have two function calls happening here.
[00:07:16] What are those function calls look like?
>> Speaker 2: Could you scroll down?
>> Kyle: I'm just asking what is line 15 look like to you? I have a listsum that's called and it's output becomes the input to isOdd. What does that look like to you?
>> Speaker 2: It looks like a compose.
[00:07:33]
>> Kyle: It looks like a compose, right? That's the instinct you wanna start looking for what is this? Well, I am gonna wanna compose isOdd
>> Kyle: And listSum,
>> Kyle: You see that? I'm trying to compose listSum with isOdd which now produces a function which is expecting a list. It will pass it into listSum, and then that result will go into isOdd, then that will come out.
[00:08:13] Now that is my filterer, right? So I need to,
>> Kyle: Curry my filter object. And then provide that composed filter, I mean that composed predicate.
>> Kyle: [COUGH] We double check to make sure this does what it's supposed to do and I didn't mess something up, which we've all ready seen I'm good at.
[00:08:49] And there we get the output,
>> Kyle: So we were able to take that mess that I started you out with and using what we already know about currying and composition and piping, to produce a point free representation. Can everybody follow that?
>> Kyle: There's one kind of final thing I was hoping maybe some of you would recognize.
[00:09:14] Which is not just that we moved it down to a point for thing but to recognize that we still fundamentally have three different repeated operation. And anytime you see that set of operations happening, one of those bells that should go off in your head is that fundamentally a list operation?
[00:09:35] Is basically pipe, a list operation.
>> Kyle: What if pipe was a reducer? What if we gave it a list of these functions and we reduced that list with pipe. So I'm gonna, I'll just leave this there in the comments so that we can see it. But I'm gonna copy it,
[00:10:06]
>> Kyle: I'm gonna copy it and edit it. So I'm gonna take this and put that into an array. I'm just going to have those three values. Those three partial functions there are curried functions in an array. And then I'm gonna say let's think about what a pipe does.
[00:10:23] Pipe could take two functions and make them into one right? Because that's what, fundamentally, composition is. So if I did a reduce with pipe, it would take the first two in the list to these two, and compose them into a function. And then it would take that one, and compose it with the third one into a single function.
[00:10:45] So the end result of that function call would be something that I could pass nuns to. And it should pass nums into that piped function which then passes it along to each of these curry functions, all one call. There's one last little detail, and this is more a vagary of how JavaScript's mechanisms work.
[00:11:09] But something that you wanna pay attention to if you're gonna use these list operations built into JavaScript arrays. Reduce, you recall earlier when I talked about how it passes in multiple arguments. It'll pass in also an index and also the array. It does that for map, filter, and reduce.
[00:11:27] It passes those extra arguments that typical functional programming libraries don't. That's gonna create a problem for pipe. Because pipe is expecting everything to be functions. And we're gonna be passing stuff to it that isn't functions. We really only wanna pass two things into it, right? Cuz we wanna it to reduce two functions at a time.
[00:11:51] So how about if we call binary on the pipe function to restrict its input to only the first two arguments?
>> Kyle: Let's test to make sure I didn't mess that up.
>> Kyle: There we go.
>> Kyle: So not only was I able to adapt list operations to objects to other data structures.
[00:12:28] Then we used what we've learned in this course about currying and piping, and composing, to build a point-free representation for that flow of data. And then we recognized that each of the steps in that flow of data can actually be modeled itself as a composition, as a piping.
[00:12:44] It's a list operation, where we reduce with the pipe utility.
>> Kyle: If I hadn't taught you anything in this course, and you just saw that piece of code. The honest truth is probably most of us would not know exactly what that was doing. We would probably struggle to understand it.
[00:13:03] At this point, I hope you don't feel completely lost looking at that code. I hope it at least has some recognition to you. But where we're at right now, I wanna recall back to the very beginning of this course, when I talked about that curve of readability. And we've quested the top now, and for many of you you might be starting to wonder I'm I headed towards this trough of, my God what I'm I looking at.
[00:13:27] Cuz that's certainly where I've been when I've been trying to re-organise with point free style and use function utilities I cross some threshold, after which things really start to kinda go down hill towards that trough. And all I can say to you, is that the first time I did stuff like this, I very much felt that way.
[00:13:48] And I understand why you would feel that way. But I pushed through, I kept trying it. I kept trying to practice with it. I kept mixing and matching that, every time I had a piece of code, I tried these techniques out on that piece of code. And now, this piece of code as written.
[00:14:04] It jumps out off the page at me as, I know exactly what it's doing. And it's not just because I wrote the exercise. It's because I've started to develop some of those instincts where this has become more readable. So I'm starting to head back up the hill of readability now.
[00:14:19] So pushing through is important because at some point you're going to get disillusioned and say like I've got no clue. This is crazy, you've got to just keep pushing through or you'll never get to that point where this really starts to benefit your code.
>> Speaker 4: In the real world, where you're on an enterprise.
[00:14:38]
>> Kyle: Wait, am I not in the real world?
>> [LAUGH]
>> Kyle: Wait a minute, I thought I was in the real world. Okay, go ahead.
>> Speaker 4: Where you're on a enterprise team with a bunch of programmers, some of whom are gonna be junior, some of whom are gonna be principle, and everything in between.
[00:14:57] Do you feel that right in this kind of code becomes a barrier to entry to those junior developers? And how do you go about introducing functional into a team? Where you have that range of ability.
>> Kyle: Yeah, so let me answer that question in two different ways. First, I'll be glib, I'll say tell them to go read my book and watch this course.
[00:15:21]
>> Speaker 2: Go on, okay.
>> Kyle: Okay, but secondly, I'll give you a more serious answer, a more intentional answer. There are lots of things that, I mean we're in a learning context, we've been doing a course where we've been learning things. And there are lots of things that you can learn in any course that other members of your team won't know.
[00:15:43] If your premise is I can only use things on my team if everybody already knows it. Then you're gonna be stuck at the lowest common denominator of that team basically forever. And I don't think that's a particularly healthy way of thinking about the culture of a team. So I never want anyone to feel like I'm saying, boil it down to the lowest common denominator.
[00:16:04] I do think everybody on a team should be learning and should be striving to understand more. I always tell people, I don't care where you're at. I only care what your direction is. If you are headed upwards in terms of your knowledge, you're all in a class and you are committed to learning.
[00:16:19] And you should be wanting to promote that same culture among the other members of your team. And there are some tools that you can use to promote that and one of those tools is code review. Code review is often or many times seen as almost a negative thing, like it's a gate or I can be criticised.
[00:16:37] And I'm afraid to put my code in and what if they tear it apart or whatever? That's a culture problem that you should really fight against. You should say, no, we encourage it and we're excited about code review, because we get to learn something every time we do a code review.
[00:16:51] If you're a more senior person to somebody else on the team and they submit some code and you do a code review. And you see something where they put a point in and they could have reduced that point and done point free style. You should say, hey, why don't we meet for five minutes and I wanna talk to you about this thing called point free style.
[00:17:09] And I wanna just show you one example of how it made this little piece of code a little bit easier to read. I'm not failing your counter view because you didn't do it, but I'm using it as an opportunity to pass along a small piece of knowledge that you've helped learn.
[00:17:23] If everybody takes that perspective, then everybody gets to learn. Everybody goes up the curve of learning. And you don't have to be scared of using new stuff and nobody being able to understand it. You probably do wanna be deliberate and maybe not go rewrite your whole code base overnight.
[00:17:39] Do it a little bit at a time, and you wanna be very intentional. You wanna communicate to my team, I'm gonna start to introduce this concept. I learned about function composition and now, I'm gonna introduce that. And this is what it's gonna look like. I wrote up this wiki page, if you're interested in it.
[00:17:54] Here's links to resources. But if you see that in my code, you know I'm doing that on purpose. And communicate that with the team. Have a brown bag lunch and say, look, here's the stuff that we're gonna try to do to improve the code style. So I have to believe, even though I'm not as much in the real world as maybe you are.
[00:18:11] I have to believe that developers want to get better at their craft. And I have to believe that there are things that we can do to promote that culture within our teams. And you can go to your boss and say, I want everybody to learn here. I've been learning stuff and I'm excited to improve our code with that.
[00:18:29] Will you support me in that effort, when I try to help raise the bubble? If all boats rise with the tide, will you support me in that? And I have to believe that a good boss will say, absolutely, what can I do to empower you with that? So I definitely think that this kind of stuff, done intentionally and done slowly, not radically, can improve a code base even in the most real world of enterprise teams.
[00:18:52] Everybody has to commit to wanting to learn though.