[MUSIC] In this segment, we're going take curried functions and call them with too few arguments and see why that's a neat thing to do. So previously we used currying just to simulate multiple arguments. If you wanted a three argument function We used currying to have a function that returned a function, that returned a function to have three arguments. But, what if we took those same function definitions and as callers just passed in fewer arguments, one or two, instead of three. Well then what we would get back, is a closure. That's waiting, if you will, for the remaining arguments. There's no new semantics here, it's just a pleasant idiom that is completely allowed, you can always call a function that returns another function, and then, save that function and have it around, for when you want to call it. So this is called partial application. It's very convenient and useful. You can do it with any curried function, and I'm now showing you any new language constructs here. So let's do a couple examples, I've already written out all the code for this segment. So we have our two curried functions from the previous segment. We have sorted three which takes x, y, and z curried, and fold which takes f, acc, and xs curried. And now let's notice Then we can just call sorted3 with 2 arguments instead of 3. So we're going to call sorted3 with 0. That will give back a function, we'll call that with 0. We'll get back another function and then when we call that function what we'll end up doing is taking in an argument z and asking is z>=0? And is y, is 0 greater than or equal to 0. So fundamentally, what we got back was a function that takes in one argument and tells you if it's non-negative. Well, that's not the most useful function in the world, but it helps out something actually useful, like summing up all the elements in a list. Here I have a call to fold where I've also passed in 2 arguments instead of 3. So what I'm going to do is get back a function that expects a list x's. Right? It'll basically be this function here and we'll fold over it using this for fn its environment and this for the initial accumulator. So this will actually sum all the elements in the list. Now there are other ways, perhaps more intuitive to you before you've seen this. Technique ways to write this function. I could write is_nonnegative by just being a function that takes in x and call sorted3 with 0 and 0 and x or sum all the elements in the list, the same way I showed you in the previous segment where we just create a function that takes in xs And calls fold with these 3 arguments. It's just longer, right? These are pleasant, we get used to it, it's a convenient thing partial application. These are not terrible, this is not awful style. But we want you to get some practice with the sh. Shorter way, that demonstrates that you understand how currying and partial application work. And in fact, if I go back to the slides here quickly, the reason why the second version is really the same as the first version, is actually something we've seen before. Just a little bit different. This top version, is just unnecessary function wrapping. Right? We, have seen before, that, we shouldn't write fn fx = g(x) when we can just write val f = g. Well its the same thing here, except instead of having the variable g for the name of the function. We have the function that you get, by calling fold with the synonymous function. And zero. Alright? So let me show you a couple more examples I have here of just currying and partial application being useful to hopefully get the hang of it. Here's a function range which takes two arguments in a curried form. And essentially if you call Range with something like 3 comma, sorry, not comma. We're not toppling. Range 3 6, that produced the list 3, 4, 5, 6. Something like that. All right? And so if you call range with just 1 number, like, say 1 That's going to give a function back, that when you call it with a number, returns from the one up-to that number. And so, count up of, 6 where we return to the left 1,2,3,4,5,6. Just another example where we didn't need this unneccessary function wrapping version that you have here. The function range 1 is a perfectly good function, we can just say val countup = range 1. As a more useful example are iterators. Are higher order functions over lists and data structures like that, are often written in a curried form. So let me show you another example, just to sneak in another useful iterator. Here's a high order function exists, that takes a function which I'll call predicate, and a list xes. And returns true. Returns true, otherwise it returns false. So there's a simple 3 line function. The empty list should return false. There does not exist an element of the list for which predicate is true. Otherwise it's predicate applied to the first element of the list Or there exists some element in the rest of the list for which predicate returns true, so that's exists. Here's a use of it. If you call Exist with a function that checks whether its argument is seven, and a list that does not have seven, you will get false. Alright so that's the result here, is false. But much more interesting, is to take Exists, and partially apply it. A way applied to one argument will get back a function that takes a list and returns the book. So has zero does indeed have type int list arrow book and when your call has zero within int list, itwill check wether any of the elements in the list are zero, because exists with this anonymous function returns the function, Returns a closure, that given a list, checks what we want to check. Similarly, the, build in library functions in the list library provided by ML's standard library, are often written in curried form. So list dot map is actually defined for us, but it's curried. So if you call it with fun X, arrow, X plus 1. You get back a function, int list arrow int list, that will, indeed add 1 to all the elements in the list, returning a new list, and List.filter works the same way in the standard library. If I call it with this function, I will get back a list that is also an int list arrow int list, is the type of removeZeros, and the list I get back will have all the zeros. Removed from it. So that's very elegant and when you go to use these functions you have to use them in a curried form here we're using partial application. but there is one thing that I don't want to talk about right now. But unfortunately I have to. So once you start using these polymorphic curried functions with partial application, you might run into this thing called the value restriction. The value restriction is something that's NML for very good reasons, the type system would be broken without it. I don't really want to talk about it right now, 'cuz it's confusing. But you may start seeing something like warning type vars not generalized. And if you see that warning about some partial application that you have, you're not going to be able to use the function to get back. And this shouldn't surprise you have not actually done anything wrong in terms of what I have taught you so far, but you simply have to work around it. And you know no language is perfect. This is an ugly fact of life in languages like ML. So let me show you an example that might happen. Use partial application all that you want for now, it's a beautiful item. But if you do something like this. Where the result would be a polymorphic function. So I'm going to map this across something. So this could take any, the resulting function could take any kind of list and would return an alpha star int list, of the same length where I put a 1 next to every element of the list, and this will give that weird warning. And, it won't be able to be called, and how should you work around this? Well, the first way is to just, give up on the partial application, and put in, what I said was unnecessary function wrapping, but now it's a little more, necessary. If you don't like that, and you really want to do partial application, then you can put in an explicit type. That's not polymorphic, like this, and then you have a perfectly fine function but it can only be used with string lists, not with any kinds of list. I should also point out, that on homework 3, and things like that, you are not likely to run across this, because it only happens when the resulting function would be polymorphic. So for example, this call will not give any kind of warning, because we can tell from this anonymous function That it can only be applied to int-list anyway. So, just wanted to talk about that, didn't really wanted to, but in case you hit the warning, I didn't want a bunch of questions, of that thing you told me to do isn't actually working. It usually works, when you get the value instruction for things like List.map, I've given you a couple of work arounds.