Transcript from the "Generalized to Specialized" Lesson
>> Kyle Simpson: So we've already seen multiple usages of Closure but I wanna take you towards one of the most important of the usages of Closure.
>> Kyle Simpson: There is a notion in functional programming of defining things in a general way and then specializing those things. So moving from generalization to specialization.
[00:00:24] So I want you to think, if I had a function that took three inputs, took an x, y, and a z, and maybe added those three things together and gave me the result. So if I call that function and I passed in one, two, three as it's input, so I'd get the value six back out.
[00:00:44] Everybody with me? That would be the general representation of that function. The most general form of it is that it takes three inputs. Now, what if some point in my application I said, I'd like a slightly more specialized version of that function. One that already knows what the first of the three inputs is.
[00:01:09] So maybe one that already knows that the first input should be the value five.
>> Kyle Simpson: That specialized function, then, would expect only two inputs. The second and the third of the original, is everybody following me or did I lose you? Too abstract? If we took the function foo that took three inputs and produced another one that only expected two inputs, and it was hard coded to know the first one is already going to be five, that would be a specialization of the generalized foo.
>> Kyle Simpson: Here is an add function it takes an x and a y, and I'm going to use, on line 11. I am going to create a new function that is a specialization of the add function And I'm going to call it addTo10. I'm going to use a utility called partial, and I'll explain that utility in a moment.
[00:02:06] But looking at addTo10, can we see how addTo10 is a specialization of add?
>> Kyle Simpson: It's a specialization of add wherein the first argument is already known, and it's fixed to be the value 10. So when we call addTo10(32), we only had to provide one input. And it already knew, somehow magically, to use the value 10.
[00:02:36] And it's not magic at all really, is it? If we look at how partial works, partial takes in a function fn on line 5. And then it takes in a list of other arguments which I've gathered up into the array firstArgs, and it makes a new function called applied.
[00:02:57] So this is yet again a higher order function. You see these all over the place now. This is a machine making machine. We return back a new function that's expecting some more arguments that I call last args. And when you've provided the first args and the last args, it calls the original function FN.
[00:03:18] It calls that FN because we've closed over FN, and it uses first args because we closed over first args.
>> Kyle Simpson: It's Closure. It remembers those things.
>> Kyle Simpson: That's how add 10 remembers not only the add function but also the value 10, It remembers those things via closure.
>> Kyle Simpson: This technique of taking a generalized function and pre settings some of these inputs is called partial application.
[00:03:54] Even if you never heard that term before partial application?
>> Kyle Simpson: We said earlier that Apply is kind of the academic term for passing in arguments, right? So partial application implies that we're only partially passing in some of the arguments, which is what we're doing here. We only passed in one of the two expected arguments.
[00:04:16] And we created a specialized function that would remember what we passed in and use that later. Here the later is just two lines away, but as a matter of fact, that closure could hold onto that for as long as we need it to. It could be an entirely different part of the program.
[00:04:34] It could be actually on a different timeline where it's in response to some event in the future. Doesn't really matter, the closure's gonna hold onto that information as long as necessary. We can also call addTo10 as many times as we want. If I said addTo10 32 and I said addTo10 11, gonna give it 10 plus 11.
[00:04:56] Call it as many times as I want. So what I've done is I've taken a utility that was generalized and I have made potentially many more specializations of that utility because I could also make an add to 11 and an add to 48, and an add to 619.
[00:05:16] I can make as many of those specialized utilities as I wanted. Just call the partial and pass in the values.
>> Kyle Simpson: This is important because it is easier for me to pass around addTo10 as a value. It's a function value. It is easier for me to pass that around where it is collected the add function reference and the value 10 together and memorized them.
[00:05:49] It's easier to pass that around than it is to pass those two independent values around separately. Add to 10 is now a thing that I can hand off to somebody and say here if you wanna add things to 10 just use this utility. And they're like, cool thanks very much.
[00:06:07] I don't have to say, well if you wanna add things to ten here's add and here's ten. I just give them the one package all together.
>> Kyle Simpson: And it's not really about giving things to people. This is about making Lego pieces in our code that we can compose the other parts of our program In a more reasonable way.
[00:06:29] So what's a practical example of this? Well, let's imagine you have an Ajax utility, something that is gonna make Ajax requests. And the very first parameter of that Ajax utility is the url. And the second parameter is the data. What if you knew a fixed set of URLs, like, you knew what the API endpoints were for your application.
[00:06:53] And so, instead of having the AJAX utility used everywhere, and you had all those URLs hard coded in. What if, instead, you partially applied your AJAX utility ahead of time, at the very beginning. And you went ahead and stored the URL in each different variant. So now, you have a variable called lookup users and that thing already knows to make the ajax call with the lookup users API URL in it.
[00:07:22] And all it's waiting for is the data. And then you have another function that you make called look up orders, and that one is closed over a different URL, and look up customers and so forth. So, we can make these functions that remember that information. And then those pieces are easier to reason about in the other parts of our code.
[00:07:44] We're not cluttered up by all the URLs. We just know, that's how I get my, that 's how I get my customer list. That's how I look up the orders. Call the function and just pass in the data. So partial application is process of generalizing, of specializing a generalized incredibly powerful happens all over the place.
[00:08:05] In function parameters it's very privelent technique.
>> Kyle Simpson: How many of you have heard currying before? You've heard the term currying. For the longest time, I thought that partial application and currying were basically the same thing. As a matter of fact I've been asked on interviews and I have asked on interviews, tell me what the difference is between currying and partial application.
[00:08:33] And I incorrectly responded, well, they're kinda just two different ways to do the same thing. And that interviewer correctly called BS on me, because that's not accurate. There is a set of similarities between these two patterns and they are set a very important differences. And if I were to ask you, what's the difference or first if I said how are partial application and currying alike?
>> Kyle Simpson: Do you think you could answer that with the crisp answer? Could you tell me what is similar about the two? Maybe. What if I said, okay now tell me how they're different? Could you tell me how they were different? I wanna try to summarize for you the difference and the similarity between partial application and currying.
[00:09:23] This is partial application and there is a lot of times I have seen this all over the place where somebody will do partial application, and they'll call it currying, or even more often they'll do currying and call it partial application. We don't wanna use the wrong terms for stuff, terminology matters cuz we wanna communicate correctly.
[00:09:46] So here's how they're the same, here's what they are alike, here is what's similar between partial application and currying. Partial application and currying are two different techniques for specializing a generalized function.
>> Kyle Simpson: If you gave that answer in a job interview, you'd probably get the job because 99.99% of your peers have never thought to articulate it specifically like that.
[00:10:16] Partial application and currying are two different techniques for specializing a generalized function. How are they different? Partial application takes some of the arguments now, which is line 11. And it takes all of the arguments later. All the rest of the arguments later, which is line 13. So you can either do it now or later, but you only get those two shots.
>> Kyle Simpson: Partial application takes some of them now, the rest of them later. Currying on the hand says, I think what we're going to do is provide them a little bit at a time. We might provide one now, maybe another one a little bit later, and maybe another one a little bit later than that.
[00:11:07] And maybe another one. Or a few later, I'm gonna sort of drip them in as I go across my code base because I actually need to do lots of levels of specialization. Add only takes two, but what if add took 12? And I wanted to make one that knew about the first three, and then another one two more, and another one that knew about three more from that, and I could make lots of levels of specialization.
[00:11:34] And if I wanted to do that with partial application, it might be a bit awkward because add to 10 expects everything on the next call. If I wanted to provide only some more of the inputs, but not all of them, I'd have to do another partial application on addTo10 to make another specialized function, which I, of course, can do.
[00:11:59] You can keep partially applying a partially applied function, and partially apply, and partially apply. You can keep doing that as many times as you want, but you're gonna have to keep using the partial utility to do that. So it can get a little bit cumbersome. Here's what currying says.
[00:12:14] Currying says I'm gonna go ahead and automatically assume that every input that you give me is just one of my inputs, and I'm gonna return you a new function. That's expecting the next input and I'm just gonna keep doing that over and over and over again, until I've received all of the inputs and then I'll call the original function.
[00:12:38] It's kind of like, automatic partial application, one argument at a time. Instead of calling partial over and over, curry is going to automatically do that for you.
>> Kyle Simpson: So say we have this add3 function. It takes an x, a y, and a z. And we pass it into curry.
[00:13:02] What we got out of that is an add3 which doesn't know about any of its inputs yet. That's different than partial. We haven't provided any inputs yet. We've just adapted add3.
>> Kyle Simpson: So add3 is a function that's waiting for an input. And we call that with the number 3 on line 5.
[00:13:23] And that's gonna return us another function which is expecting another argument. So I could immediately call it, or I could just save it off into a function called f. Do you see how f is a specialization of add3?
>> Kyle Simpson: f is a function that will eventually call add3, but it already knows about the first of its arguments.
>> Kyle Simpson: So then later, I could call f again and give it another input. And I could store off that result and now P is a specialization of F which is a specialization of add 3. You follow that reasoning? I just create more and more specialization every time I provide a new argument, what I get back is a function that's more specialized than the one before it.
[00:14:16] And eventually, Line 9, I provide the third and final expected input, and it calls the original function under the covers with all three inputs, and I get back my result 12, 3, plus 4, plus 5.
>> Kyle Simpson: So you notice I didn't need to use currying on any of line 5, line 7, line 9, or line 11.
>> Kyle Simpson: I only needed the curry once because currying says, I'm gonna go ahead and assume that we're just gonna keep partially applying over and over and over again. It automatically does that for us.
>> Kyle Simpson: So I can string them out as separate ones like I did on lines five, seven and nine or I can do it altogether like I did on line 11.
[00:15:07] Does line 11 look familiar to you at all?
>> Kyle Simpson: Does that look like something we did in the,
>> Kyle Simpson: Point-free exercise earlier on in the course? I gave you that printIf function. You remember you call the printIf the first time you gave it an input and it gave you a function back that was expecting the next input.
[00:15:29] And then that gave you another function that was expecting the next input. All I did there was manually curry those functions. I didn't automatically curry them because I hadn't taught you currying yet. But now, we see we did that manually and now if we were to go back to that exercise, we could go back and use the curry utility to do that automatically for us.
[00:15:52] We could make a function that takes all three inputs and then, just curry it and have the same capability.
>> Speaker 2: Is there a way to manually sub the curry function out? It's not always built in.
[00:16:20] But you can define it yourself or use a library.
>> Kyle Simpson: There is a utility built in the Java Script, that does partial application, it is the bind utility the unfortunate part about the bind utility like if I said foo.bind is that the first argument to bind is for this context.
[00:16:44] And the subsequent arguments are for partial application. So what they did is they took these two really useful concepts. This context, partial application, they stuck them together in the same utility. Now, there have been many times when I've needed this context binding. There have been many, many more times that I've need partial application.
[00:17:40] So I could have said, Add dot bind null comma ten and now I would get the add ten utility.
>> Kyle Simpson: But we don't have a built in curry yet. I wish we would. It would be nice and useful but for now, you're just going to have to stick to using the ones that are built in to functional programming libraries.