Transcript from the "Monads" Lesson
>> Brian Lonsdorf: So this is the introduction to monads. And we will do this in the next ten minutes, so that's pretty great, okay.
>> Brian Lonsdorf: How's everybody feeling? Should we take a ten minute break? Okay, all right, let's do it. So we have of. Now, it takes an a and puts it in some kind of type, some kind of container.
[00:00:27] Can you guys see that? It's got an a, and now we have an F of a. Basically takes an a, puts it in something, whether it's an identity or a Maybe or something. That's what of does. Why not just the constructor function, you might ask? [LAUGH] Like, I have Maybe, why don't I just put it in a Maybe?
[00:00:46] This function, of, is also known as pure, or return, or unit, or point. It's a pointed functor. Anything that's a functor, anything with a map method and an of method is a pointed functor. Now, you ask, why might I do of instead of just put it inside the function itself, or the constructor, just put in the box?
[00:01:10] Why not? Well, if I want to put the future of this function, [LAUGH] of the future of dubstep, which is inside. A future doesn't take that. The EventStream we went over a little bit more. That EventStream is actually expecting a DOM element or something. It doesn't really expect a function.
[00:01:34] But we wanna put a function inside it. And for reasons unknown right now. But the idea is that this of method is better than a constructor. Because it'll put whatever you have into the context. It doesn't even have to be what it expected in the first place. So if I have, I don't know, some crazy observable or something, and I wanna put something in it.
[00:01:59] This is better than calling the observable. For Promise, for instance. If I want to put a two in a Promise. If I say Promise of two, I get a resolved Promise that I can map over instantly. So it's better than the constructor for lifting things into it. That's what a pointed functor does.
[00:02:16] And we'll use that with monads. Dun, dun, dun! [LAUGH] Just take it in. [LAUGH] So if you understand functors, and I hope you guys are getting it. I hope you guys really kind of feel the functor intuition. All this does is join them together, stupid simple. A monad is a pointed functor with an extra function called mjoin and/or chain.
>> Brian Lonsdorf: So the types of mjoin takes this, something with a Container of a Container of a. It just give you a Container of a. We've got this nested thing that returns me one thing. Notice those are the same containers, they're both M. chain takes a function from a to a Container b in a, and then returns you a Container b, or Container of a to Container b.
[00:03:11] We'll look at that later. But there it is, a pointed functor plus one of those functions equals a monad. Well, I say one of those functions cuz you can define one function in terms of the other, which is pretty neat. So I think I still have that aka from the last one.
[00:03:28] That's a mistake, we'll fix that. So if I call mjoin on a (Container(Container(2))). I don't have the answer. I'll get a container of 2. I don't have two Containers anymore. It will flatten them like a list, not bad, right? So a (Container(Container(2))), and I mjoin it. I'll just get a Container of 2.
[00:03:51] So lets look at this. So if I find the order and I have two Maybes, it's going to grab me an order from the API. And then it's gonna grab the tracking_id off of that. And I've got a Maybe and a Maybe. At this point, since both findOrder and getTrackingId both return Maybes, findOrder gave me a Maybe of an order.
[00:04:12] Then I mapped over that and got another Maybe, so I had a Maybe and a Maybe right here. So doing that, I have to map over the first Maybe, map over to the second Maybe, and then render the template. This is common. If you start combining contexts and composing context, this is will happen to you.
[00:04:32] I'm sorry, you have to give up there. [LAUGH] Or you don't, you could just use this mjoin. That's it, that's all you need. So at the point where it gets nested, we just join it. So this will take a Maybe Maybe to just a Maybe. So you guys are familiar with listFlatten, right?
[00:04:55] It's like you got lists, and lists, and lists, and you just flatten into one list. Well, here we have a Maybe in a Maybe, flatten into one Maybe. Who cares? Not that hard. And that's all monads are. And you don't even have to think about it in advance.
[00:05:08] You're just like, no, I'm nested, mjoin, done. So that's cool. Let's see another example. We've got two IOs. This getSearchTerm is gonna get it out of the location, so we want to make sure it's safe. It's an IO, you don't wanna run it yet. We've got this set the input value, so if we grab the param off the location query string, and we put it in the value.
[00:05:32] We got two IOs going on. We get our SearchTerm, map that. We've got an IO of an IO, right? Well, if I just, in here have to map runIO over the first one, which is hilarious. And I end up with another IO that I have to run, it just is weird.
[00:05:47] So here we just mjoin it, done.
>> Student: So that's just an additional, I don't know the word, function I guess that you're passing that will get executed after the map.
>> Brian Lonsdorf: Yes.
>> Student: You just toggled back and forth, I just wanted to make sure.
>> Brian Lonsdorf: Yeah, sorry.
>> Student: Yeah, so mjoin is being dropped in as a first or last.
>> Brian Lonsdorf: And look at the IO(IO(Dom)), IO(Dom). IO(IO(Dom)), IO(Dom). [LAUGH]
>> Student 2: Someone had a question about promises. He was saying they don't resolve immediately. But yeah, they run immediately.
>> Brian Lonsdorf: I see.
>> Student 2: So the differentiation between IO and Promise is that IO doesn't run immediately.
>> Brian Lonsdorf: But Promise is run immediately.
>> Student 2: Promise is run immediately.
>> Brian Lonsdorf: That's fair, yeah, yeah. I wasn't careful with my words there, yeah. Promise and Futures here are like an IO-ish Promise, where it doesn't just start executing and eventually run. But you're right, so to clarify. Yeah, so we just drop the mjoin in and we're good.
[00:06:55] And if everybody knows how to do that, you're using monads. Now you gotta point out that mjoin is just like map in that it's going to just call whatever on whatever object it gets. If it gets a Maybe Maybe, it's gonna go to Maybe and say, tell me how to join you.
[00:07:08] I don't know how to join you. So these things have to be monads as well, right? Luckily, all the types we saw were, and just about every type will be working with kind of is. But maybe not Either, yeah it is. It's a monad. Okay, so-
>> Student: Brian, there's a question.
[00:07:26] Moses is trying to clarify, so is mjoin a monad, or is monad a result?
>> Brian Lonsdorf: I'm sorry. Maybe, EventStream, IO, those functors. Those are also monads. If we go back a few, that's what a monad is. It's a functor that's pointed, meaning it's a functor that has an of method and join method.
>> Student 2: Then mjoin just recursively calls them out, kind of.
>> Brian Lonsdorf: Sort of, like if you think about Maybe, it would say, I have .val. And if it's not null, I'll pass this value. It takes care of it for you. You just tell Maybe like, I've got a Maybe and a Maybe, and I just want one Maybe.
[00:08:09] Can you tell me how to safely do your null stuff and make sure there's only one? So it's two notes.
>> Student 3: So in that other example where we get triple field, it'll take care of that too?
>> Brian Lonsdorf: Triple, yeah, three things? That only happened. You have to mjoin twice.
[00:08:25] [LAUGH] No, that's not a big deal. Because there's ways to compose monads, and we'll see that. So you're not typically ever mjoining twice. [LAUGH] What's up?
>> Student: We're wondering, what's the point of an of method? What does it mean to have an of method?
>> Brian Lonsdorf: It is almost exactly like a constructor.
[00:08:49] But it puts a value into the context, not necessarily what the constructor is expecting. So like I was saying with an EventStream. An EventStream of clicks is like, what do you even, do I call the EventStream constructor with my function? I don't understand what to put in my EventStream constructor.
[00:09:10] Of lets you put anything in there. I could put the number two in my EventStream constructor. So of will put anything in your context for you. What it does is lift any value into an EventStream and start working from that. And the EventStream needs to know how to do that.
[00:09:25] That's what the of does. And this mjoin tells it how to, if I have EventStream in an EventStream, just make it one.
>> Student: Would you ever use of if you had an unknown functor? Where you're like, I don't know what this was. I don't know what constructor to use to make another one, but I could make a value.
[00:10:00] That way if I get a return type and I don't even know what it is, I can call of on it. It's a dynamic way to put it in whatever context I got. I don't need to hard-code my type into this computation. I could get some value and call of on it, which is really neat.
[00:10:15] So you can imagine that you're composing. And then you get some type, some functor, I don't know which it is, but I'm going to call of on it. And I should point out that switching functors changes your whole app. So if your app is working with the Future, switch your Future to a different library, still works.
[00:10:34] Or switch it to a Maybe. Now it works with a Maybe, it runs if it's there. So your app is generic enough to work on any functor for the most part, usually. So unless you're calling explicitly fork in your app, which you're probably doing right after the very last line.
[00:10:47] All right, so let's look one or two more examples and get into the code. It's not that hard. It's just drop mjoin in there. [LAUGH] So it's not that crazy. Functors were the hardest thing in the class, and we got through it. You guys were alive. [LAUGH] Here's one where we read a file, and then we upload it to the server with this fictional httpGet.
[00:11:11] Which will return as a Future, because it's going to the server. So, reads the file, goes to the server. We've got a Future in a Future, we join them. I can fork once. I don't have to fork and then fork the success. I don't know what to do with the Future of a Future.
[00:11:26] Here's another one. Why don't I ask the user what they want? You can imagine in a console environment, or a node or whatever. We'll ask the user what they want, that's a Future value. Then we'll map read, ask what file do you want. We'll map(readFile) over it, and will join those two Futures.
[00:11:41] Then we'll send to the server, and again, join those two Futures. [LAUGH] So you see this pattern of every time you do a monadic action. That is, every time something returns a Future that you're mapping over, you're nesting these Futures. And by joining them, you're putting them back together into one.
[00:12:03] This is three nested callbacks. If you look at at node app, it would be this disgusting [INAUDIBLE] inward avalanche towards the scroll bar. But right there it's just it looks like it's not in the Future. It looks like this linear thing.
>> Student: Two question on this slide, Brian.
[00:12:22] Robbie V is asking, can you mjoin funct doors of different types? Like maybe in an IO?
>> Brian Lonsdorf: You cannot, you need to use a lens or a monad transformer, or another construct that will allow you to do that. Typically, the solution would be to make your own Maybe IO monad with a monad transformer.
[00:12:45] Or better yet, use the lens library to just define a double map. But that's kinda beyond this class.
>> Student: Daniel B is asking, does fork return a Future?
>> Brian Lonsdorf: No, fork takes its success and error function.
>> Student: Okay, and it operates on a Future.
>> Brian Lonsdorf: Yes, it returns kind of null, it's the end of the line.
[00:13:09] We're done there. Okay, one thing to point out, did I have my code? Where is the example? It's there, yeah, here's chain, just so you know. It's just fmap mjoin, not that hard. [LAUGH] Where fmap, mjoin, fmap, mjoin. All we had to do for fmap mjoin was just chain it.
[00:13:34] And you've heard this as flatMap or bind. That's what chain is. So if you look at that, now we ask the user. And we chain readFile and chain sendToServer. And it's if you just drop in replacement for fmap, mbind. And you'd see the same thing as flatMap. Different names, same stuff.
[00:13:50] The reason we call it chain in the Fantasy Land spec is because if you do the dot syntax, it's literally chaining. [LAUGH] So yeah, this is the same thing, but dot syntax versus the super sweet functional point free stud. All right, [LAUGH] okay, so.
>> Group: [LAUGH]
>> Brian Lonsdorf: One last thing I wanted to tell you guys before we jump into the examples.
[00:14:17] mjoin is defined by chain(id). Basically, chain says, open up my monad, open up my container and pass the value into it. And id just says, return the value. [LAUGH] So that's mjoin, whereas chain says, first fmap, then mjoin. So both of those can be defined in terms of each other.
[00:14:37] So any monad can be defined with one of those two functions. And the other one is just defined based on the other function. It's pretty neat, okay.
>> Student: Hey Brian, before we take off, Scott S wondered, does Either dot of take two parameters?
>> Brian Lonsdorf: That's totally smart. I think it only takes one.
[00:14:59] But I don't know that question and that answer. I don't work with Eithers all that much, that's why I always get it wrong. [LAUGH] So we're into all this futuristic stuff. But yeah, that's a good question. You know what you could do is you could always look up the Haskell definition, or Folktale is what we're using.