Functional-Light JavaScript, v3

Advanced Point Free Solution

Kyle Simpson

Kyle Simpson

You Don't Know JS
Functional-Light JavaScript, v3

Check out a free preview of the full Functional-Light JavaScript, v3 course

The "Advanced Point Free Solution" Lesson is part of the full, Functional-Light JavaScript, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Kyle live codes the solution to the exercise.


Transcript from the "Advanced Point Free Solution" Lesson

>> Kyle Simpson: Okay, let's talk about part 2 of this exercise. I trust you found that sufficiently challenging. [LAUGH] All right, so we said right before I gave you the exercise that we can look at things like listSums, value being passed into isOdd, that's a composition. So the point-free representation of this particular function is just the compose of isOdd and listSum, so let's just simply write compose (isOdd, and listSum.

>> Kyle Simpson: And there's our compose utility. It takes a list of functions and it uses reduceRight on those functions, on that list of functions, okay. So that made some good progress, we were point-free there. Let's look at this particular one. We have something that takes in a list and then passes that to listProduct.

Does anybody spot using equational reasoning a way for us to use a point-free refactoring here?
>> Kyle Simpson: What do we know about the shape of this function compared to what we can see about the shape of that function?
>> Speaker 2: [INAUDIBLE]
>> Kyle Simpson: They're both the same, so what does that mean?

>> Speaker 3: You can get rid of one of them.
>> Kyle Simpson: If their shapes are the same, they're interchangeable, which means we can simply pass in listProduct.
>> Kyle Simpson: That's good progress. All right, our final one that we look at here, we see a function that takes a value and another value and it just adds them together numerically.

So it's just doing a numeric addition. Well, if you looked through the functions that were provided to you, we have just such a function and it's called sum. So we don't need ostensibly if something like that was provided to us by a library, we don't need to define our own, we can just use sum.

Okay, we definitely have made some improvement with point-free. But we still have a bit of imperativeness here. Because we are still calling a function, grabbing its value into a variable, then calling passing that to another function and then passing that result to another function. What's it called when we have one functions output becoming the input of another function?

>> Speaker 4: Compose?
>> Kyle Simpson: That's a composition right? Well, we could use compose here but since these are already listed in this order, why don't we take this as an opportunity to practice with the pipe function? Which again, that's provided to us down here, we have the pipe, it's just composed with the functions reversed.

So let's think about a pipe call where we're gonna be passing in a set of functions. We don't need to be saving these into individual variables, but we're gonna be passing these functions.
>> Kyle Simpson: Now, there's a problem which is that each of these is already receiving the value.

And we want the value to come through via the piping, right? And what we're gonna pass into pipe is that nums object. So we want nums, we want this object to sort of flow through. So we don't wanna be passing it as an argument, but now we have a problem which is that those functions are expecting that argument.

If we look at filter object, filter object is expecting the O, map object is expecting the O, and reduce object is expecting the O. So what could we do with these to allow us to provide one of the inputs, but defer the other input to later?
>> Kyle Simpson: What's that called?

>> Kyle Simpson: Currying, so why don't we use our curry utility, which if you look down here, the curry utility, the curry utility takes the arity and the function. So we're gonna say 2 and then the function, but don't miss the fact that I've already made curry a curried function.

I used curry on itself, so we don't have to pass them in as a single set of arguments. We can pass them in separately. So I'm gonna say curry(2) and then pass in this function and then curry(2) and pass in this function,
>> Kyle Simpson: And then curry(2) and pass in this function.

Now ideally, those functions would've already been curried, and you remember I said earlier, it seems like a lot of times we just end up preferring currying. We sometimes ask ourselves why aren't all functions already curried? The more we end up having to manually curry things, we wonder, why can't just everybody have these functions curried?

Okay, so now we have a list of functions where nums will flow into this waiting function, whose output will flow into this waiting function, whose output will flow into this waiting function, whose output will be the number 3886.
>> Kyle Simpson: But there's one final refactoring that we could do here.

This is good enough, this is pretty declarative but there's one final refactoring that we might be able to observe. And it's gonna take advantage of the associativity of composition in piping. We could observe that, and this would definitely be true if we had like 8 or 10 of these in a list, is that manually specifying each one of these functions and then passing them into pipe as individual arguments.

That's one way of doing it. But another way of thinking about this is that this particular operation is itself a data structure. Each one of these individual values being passed into a function, if we think about that, that's a very repetitive thing. Where we're doing something and then doing it again, and then doing it again, and doing it again, that sort of almost begs for a list operation to model it.

So the way I'm going to refactor this is I'll copy that.
>> Kyle Simpson: What we're going to say is that we could actually do a list, an array of these functions, if each of these functions was in an array, I shouldn't add a comma. If each of these functions was in an array, remember we're looking for opportunities where we can use these list operations to model our programs.

And in some cases, a program itself is the data structure or the structure that we want to iterate over. So I've got a list of this functions that I want to pipe together, right? Each function I want to compose with the next one through pipe. So what's does that sound like if I wanna take two things to make it into one, and then two things and make it into one, then two things, what does it sound like?

>> Speaker 5: Reduce.
>> Kyle Simpson: Sounds like a reduce. So the function, my reducer would look like an fn, we'll call it, reducer (fn1, and fn2). And we're gonna end up calling, actually let me reverse that, no we'll do fn1 and fn2. We're gonna end up calling fn1 and then passing it into fn2.

And as soon as we realize we're basically reimplementing the pipe function, so our reducer is basically just pipe. But there's one last little nuance detail. JavaScript's reduce passes to the reducer more than just what we've been thinking about, more than just the accumulator and the value. It actually passes in four arguments instead of two.

This is true for JavaScript's map, filter and reduce, it passes in additional arguments. In this case, it passes in as the 3rd argument the index of the value, and as the 4th argument, the original array. So that function, if I were to write it out would take in x, y, z and w, where x is the accumulator, y is the v.

This would be the index, and this would be the original array. It passes in all 4 of those arguments rather than only 2 of them, we've been looking at reduces that only pass in those 2. So there's a problem here, which is that we have the desire to pass in a function, it should only receive 2 arguments, but it's gonna be passed 4 arguments.

How could we sort of restrict the inputs to the pipe function to be only 2? If you recall back to the very beginning of our course when we were talking about adapting argument shapes, one of the adaptations that we talked about was, take a function that expects some number of arguments and change its arity.

And you remember we looked at the unary utility which takes any function and turns it into a unary. And we look at the binary utility which takes any function and turns it into a binary. So the final thing that we need to do is take our pipe utility and turn it into binary, so that it can receive just 2 functions and pipe those together and just 2 functions and pipe those.

Finally, what is the end result of this reduction? It is the function that we have composed and we need to call it with the nums.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now