In this lesson, you will see how map
takes our elements of a list or an array one at a time, performs an operation and returns us a new object.
You will then learn the inner workings of map
by defining our own map
function. In the process, you’ll see how we just created a functor in PureScript. We will explore what this means as well.
Instructor: [00:00] We have a list of ints. We have one, two, three. Let's play with that a little bit. On line 15, we'll add map, and we'll give a lambda x/a, arrow to a plus one. On the right-hand side, we look at our log, and we've got two, three, four, and nil.
[00:18] Our map has taken our list of ints, and to each one, it's added one. The map has taken our elements of a list one at a time. The first one'll be one. The a would be one, and then one plus one, which two, and so on. Then it returns us a new list with new values of two, three, and four.
[00:34] Let's have a look at the type of map. Map, for all a and b, and it's a function of a to b, to f at a, and returns us f at b. We'll clarify this more by making our own map for our list. We'll call it map prime, and it has a type of for all a and b. It's a function from a to b, and it goes through a list of a, and returns us a list of b.
[01:01] If you look at our previous type definition of map, all we've done is changed the f into a type list. On the next line, we'll do map prime, underscore nil equals nil. Whatever given function, if we receive a nil, we just return on a nil.
[01:14] On the line after that, we'll do map prime at f, open close braces, x-colon-xes, and that equals fx-colon, map prime f, xes. Let's explain the first part. x-colon-xes, what's happening here is the x actually equals the one in our list. The xes would be the rest of the list, which in our case, is two, three, and nil.
[01:37] Let's explain the colon after fx. That's actually an infix operator for cons, which is a function that attaches an element to the front of a list. Then after the colon, we're using map prime, f, xes. We're using recursion now to pass the rest of our list map prime, and so on, and so forth.
[01:56] Let's just type this out quickly, and it should make more sense. We pass our list into map prime, and our function is plus one. We're doing one plus one, then the rest of the list. We do const two, two plus one, const three, three plus one, and then nil.
[02:11] Let's make sure that our function works. We'll change map to map prime, and yep, it works. We'll go back to our original map, and remove the prime. Then we'll pass in an array of one, two, three. Now, look at the output. It's an array of two, three, and four.
[02:27] If we look at a type definition of map prime on line 15, all we've done is changed a list to array. Map's giving out the structure. If you passed it in a list, it'll return us a list. If you pass it in an array, it'll return us an array.
[02:40] Let's change that array into strings. We'll do a, b, and c. Oh, but we get an error, "Could not match type int with type string." That's because it's using a plus. What we'll do is, we'll change that to append, and we'll do string b. The results will be an array of the strings, ab, bb, cb.
[03:00] What you might not actually realize is, what we've created is created a functor. Let's have a quick look at the docs. A functor is a type of constructor which supports a mapping operation, map. A map can be used to turn a function a to b into function f(a) to f(b).
[03:16] Like I mentioned before, we're running a function against the inner structure, but keeping the outer structure exactly the same. For something to be a functor, it has to satisfy some laws.
[03:24] There's a law of identity -- if you pass it ID, it will return you exactly the same thing -- and there's the law of composition, which says if you pass a function, g composed to f, into map, that should equal map g composed into map f. Let's show you these two laws in action, now that you've got a much better understanding of what's going on.
[03:44] Let's create a function called mapIdList that has a type of list, int, and on next line, we'll do mapIdList equals map, pass in a function ID, and pass in my int list. The function ID is literally a lambda from a to a. Whatever you pass it, it will return it to you.
[04:00] Let's log this out, mapIdList. Looking at the results, we've got exactly the same list, which is one, two, three, and nil. That law is our satisfied. Our outer structure is the same, and our inner structure is the same.
[04:14] Now, let's test composition. We'll create a new one called mapCompList. That's got a type of list to int. On the next line, mapCompList equals map, braces, we'll pass in my int list. Inside of there, we'll create two empty functions, and compose them together with the three arrows. I'll explain that in a moment.
[04:35] First, we'll make a lambda a to a plus one. Then in our second function, we'll do a lambda a to a plus two. What the compose is doing is, it's saying we're going to pass in our first element of our list, and it's going to go into this function. Then the result of that will go into that function.
[04:56] We'll log that out, so mapCompList. Looking at the results, we've got four, five, six, and nil. I'll walk you through the first element. The a would actually be a one. One plus one is two. We pass on that result, so the a here would be two. Two plus two is four.
[05:14] Let's change things back. Let's make our second mapCompList. That's got the same type of list to int. On the next line, mapCompList. We'll do braces, and we'll pass in my int list. Map empty function, which is composed with a map of an empty function.
[05:32] Let's fill in the first one. We'll do underscore plus two. The underscore is actually the equivalent of a lambda a to a. Then we'll do in a second one, underscore plus one, which is exactly the same thing. That's not working.
[05:46] We need to add our tick, mapCompList prime, and we will go down here, copy that, and paste it in. Then make that into a prime. If we look at the output, they're both identical. That fulfills our second law of composition.
[06:02] Having map with a function that composed from one function to the other is exactly the same as having a map and a function composing into another map and a function. If we go back to our docs, you can see that if you pass map a function which is composed from g to f, that should equal map g composed to map f.
[06:22] There you have it. You've learned all about functors and map.