What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Hardcore Functional Programming in JavaScript
by Brian Lonsdorf
Learn to apply techniques from the forefront of computer science research to solve practical problems in JavaScript.
Resume CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
The Silence
Introduction
(music intro) Alright, welcome for real. So, all these people here as well as people online, I think it's really cool because what we're coming here to show you is a really fun technique of programming in general, not just in JavaScript, and only lately has it come to the JavaScript world. But, through the exercises we're going to do today and doing it in JavaScript is going to prepare you to do it in all kinds of languages, like Haskell and Scala and Clojure and all that kind of thing. And, in fact, you know, the title is Hardcore Functional Programming, so, of course, we took our stuff from Haskell, the most hardcore of all the stuff, and we're going to use a style like that in our thing. So, in general, I think if I were to distill it into one message of the theme of everything we're doing, it's The Soul of Functional Programming, and maybe programming in general is separating the concerns of what you're doing and recognizing when those separations are happening. You know, we're used to just kind of being real programmers and just doing everything at once. Like, you've got tests you're doing and things you're writing and things are changing. And I think we need to separate what we're doing, or if we separate what we're doing, it becomes a lot easier to recognize patterns that happen again and again so you don't have to write them again and again, the way that your libraries can be built and the way they can compose gets a lot stronger. So, I'm not going to be yapping at you the whole time. This is just how it's going to start. There's going to be some exercises we get into, and it's going to be fun. So, this is kind of the general structure of the course. I'm going to be teaching The Silence. So, this is the first bit. And with the tools in The Silence, The Silence is going to deal with the way we recognize and separate things, you're going to be writing code that's so pure and so good that Brian is going to take you on The Voyage, which is where we take the code that you wrote in this new style, and you can take code that doesn't know things about the disk or the network or whatever and just transport it in these little vessels to do like whole new things in whole new lands. And, finally, The Demo, where we're going to live code and just stumble over ourselves, and it's going to be a lot of fun and make a real app using this stuff. So, let's begin at the beginning. In the beginning was primordial soup, that's how I think of it. Just anything goes. If you look at this stuff, it's like printing and testing and goto and, yay, we get to do anything because we're so good and we're so real and we're just proud of all the stuff we can do. So, surprisingly when people were arguing, you know this goto thing, it seems kind of wild west, it seems like it could be confusing, people like Dijkstra were saying that. Everyone was saying, No, we can handle it. So, you end up with a lot of names and changing. Like, you're changing variables and things, and you're trying to track down, Did I run things in the right order? When did this happen? And so, like I said, if this slide advances, some people were like, Goto is kind of a bad idea and, in general, we should be more disciplined in the code that we write. So, exercising restraint can feel weird. So, there's going to be things that we show you and examples that you might be like, Man, this is kind of weird. Couldn't I just do this in a line or whatever or some other way? And when Brian takes you on The Journey, you're going to be like, Look how nicely this fits into my sailing ship once I've written it this way so I can go on The Journey. So, there's---I'm saying goto is kind of weird. And nowadays, we might look back and be like, Yeah, those were the dark ages. Think of the stuff they were doing back then. I wouldn't do a goto. We know so much more, but I feel in a way like we're still in the goto age. We just have different things that bug us, and when you're in the day to day, it's hard to notice that this is something that can change, this is something we don't have to live with. And I think the way it manifests itself, to me at least, these are some things, some symptoms I feel when I'm coding, and this is telling me, Yeah, I should be separating what's going on. So, custom names everywhere, having loops with weird names, which we'll get into, doing loops again and again. This is like the prereqs of the course for knowing something about map and reduce and filter and things. And if you know about those you're like, Yeah, a lot of the loops I see are really just one of those, but I just happened to put an index called i, and I'm looping over stuff, and it's kind of irrelevant. So, looping patterns. Glue code, which is like---which we'll see, we're putting functions together, and we're needing to come up with more names for the arguments of things. It's just the way things glue together is very---even down to like, in my opinion, some MVC stuff where you're having to make controllers that duplicate the names of things in your database, there's just a lot of glue and a lot of duplication. And, finally, side effects, which are similar to goto, I guess. We have to remember what's going on and keep track of our stuff. So, talking about the names, this is just a work cloud I generated from some random Ruby library nice and object oriented, lots of files. And it has---these are just names from it. And what I've noticed is that the names that appear a lot happen because they're in a do block, which is like a Ruby loop, like each do---apparently, it's going through lines of files and repositories and stuff is modified because there's a lot of stuff changing. So, when I see these words themselves, they're suggesting to me that, Oh, yeah, this could be improved.
Omit Needless Names
The first bit of The Silence is to omit needless names. And the way we're going to do that is, like I said, through the separations and recognitions. So, this is maybe a little small. I can zoom in at times so we can see it. But this is an example of the first type of separation. You'll be seeing these a lot. So, the first separation we can do is to think to ourselves, We have functions, and we would like to separate the things the function is acting on, its inputs, from just random things in the environment. Like, if your function wants to, it can go ask for things. It can just go out there, and from the signature, from the arguments, it's not really apparent what it uses. It'd be really cool if, as we'll see, what it uses is explicit. So, here we have two functions. The one on the left is daysThisMonth. So, it does some date calculations and just tells you how many days are there this month. But it has a secret input, which is time itself, which is kind of a weird way to think about it. If you've ever used Ruby libraries, they have a thing called Timecop, so if you want to write a test, to make that test reproducible on a function that works with time, you have to fake out the system time or like, Just kidding, it's a year ago, I run my code, I hope it works, instead of making it really explicit. This one on the right always works the same, daysInMonth, because you just give it the year and the month. So, this is our first---going to be our first coding experience, so you don't have to listen to me. We can go to jsbin.com/yoyip, and when you first go there, don't worry, it's going to be totally blank, but it's alright, that's just the way it works. So, I'm just going to make this a little smaller. Probably close my mail and stuff. So, here's how it looks. Blank, scary, but if you hover over the upper right, it says Edit, see Edit in JS Bin. Now, JS Bin has a lot of things we can look at, but for the purposes of what we're doing, we should just be seeing the JavaScript and the console. It's kind of wrapping a little bit. I'll zoom in a little later after I read the comments. So, there're some tests in this file, and when you press the Run button, it's actually going to say the tests are passing. But that's just because this test is going to ask you to uncomment a line, and when you uncomment it, as you'll see, it's going to break. So, just the gist of it, and then I'll let you read it, is that we want to try showing how a function which very clearly separates its inputs and acts reliably on those inputs is really easy to test compared to one that doesn't. So, what we have in the file right now is a way to test that daysThisMonth thing. So, this code, as gnarly as it is, I think the point of this exercise is just to look at how we're testing the complicated one. We'll be like, Oh, we don't have to do that. So, it'll be your job to write the test for the nice one, which is daysInMonth. At the bottom of the file---so here's the structure, the top kind of gives you an overview of what's going on, and then there's something you have to do and then there's background code, and you can look at it if you like as needed. Some of it's just boilerplate that's going to make the stuff run. So, give it a try. Try and read through the file and just shout out any questions. Adam's asking what you do with the code? There's a---in the middle where it says equal and it's commented out, right above All tests pass. Just play around with that thing just so you get a feel for JS Bin and play around with it. Yeah, that's mostly it. We're going to be using this thing again. Some of the new exercises---or the later exercises will be more involved. The test will fail. But this one is this line here, assertEqual daysInMonth???, we take this out, and the point is just to fill these values in so that it passes. Ultimately, it's just to say the way we would test daysInMonth is as easy as saying assert that a certain month and year has a certain value. We don't have to mock things the way we did above. So if I assert 2014 March 31, it says Uncaught expected 30.95 and then a whole bunch of numbers = 31. Some of my old notation's bad, but that'd be extra credit to fix that. But we could just pick a different month I guess. Which assertion library is this? We're just writing really stupid, simple--. It's my one-function library called one assert. Everything that we're running is in this file? Pretty much, except for the stuff, oh wait, maybe in this one, but in other ones, we're going to pull in some libraries. This one's pretty basic. So, there's nothing hidden other than what we see here? There's an HTML, the JavaScript. Later ones will include other libraries using---if you open HTML you'll see I'm just pulling in the script tag from the JS Bin. How's everybody feeling in the room about this one? Do you guys understand JS Bin enough to play around with it, or is there something bugging anybody? I guess if you're to the point where you run it and you're getting some kind of error because you're filling something in, at least it's like, Yeah, I kind of feel I know how to fill stuff in and I'm running it. So, this is the first kind of separation. There're going to be a lot more. Did you want to walk through the solution? That's a good idea. So, my solution or the way I was thinking of it was in order to test this thing, we have to fill in some kind of value. So, 2014, let's not use the one that me and Brian brought you. Maybe, like 1, like 30 days has September, so it's 31. That'd be a much easier limitation than doing date stuff. This is so fast. Let's just make sure that the tests are---29? Okay, cool. So, to test things that make---that behave predictably on their arguments, you just pass arguments of your choice. To test things that have secret inputs, you have to mock out the secret inputs in some way so you can know what's happening inside the function to a greater degree. It's also worth it to mention that this one's probably way more likely to work both on the server and the client and other JavaScript environments rather than one that's rooted in a specific secret input environment. Absolutely. There's a request for you to speak up a little bit from Nuno. Absolutely. Is this a little better. As Brian was saying, if you make your inputs more explicit, not only is it easier to test, but as the next few slides will show you, there are other benefits, such as it's more likely to run in other places. So, you can share your code easier between the server and the client to the extent that you can. If you're trying to use the location on the window, you're just---you're out of luck. You just don't have it. But for other things, it's better, like time. This would work you know, either way.
Separating Mutation from Calculation
So, the next one is separating mutation from calculation or, at least, isolating the places that mutation happens so that---because those are the tricky parts to reason about because the order you do things matters. So, if you can make most of your code kind of pure and then at the very end or wherever plug in the parts that change, it's going to help out. So, for instance, in this case, the scenario here is kind of a simplification. So, imagine you have a blog online and you have, oops, I made it advance the slide. Imagine you have a blog and you have the articles in your blog and there's a front page, and there's read more for each one, and there's kind of a teaser where it shortens it. So, we're going to make a function where you give it a div or something and it just shortens it down. Now, on a real teaser, you'd want to break at the---not cut a word in half or, hopefully, even not cut a sentence in half. But for the purpose of this simplification, our teaser function, you just give it how many characters and it elements on the page. So, we have teaser, cut it down to 25 letters or whatever and do it to this div. And so we have these kind of imaginary functions, and some of them are not actually imaginary, we'll get them in our libraries we're going to be using. But in this one, we have---so how will this teaser work, the one that just goes out and changes things? It says, set the text of a certain element where you're slicing the string down, so you get the text of the element, text---actually, I can point with my pointer. So, this guy says grab the text out of the element and then slice it down to size from 0 to the thing, and then take that and set the text of the element with the sliced down version. I mean that's pretty simple, right? How are we even going to fix that up. It seems like that's about all you can do. But if we go down, we see, Where's the mutation happening here? If I were to say over all the paragraphs on the page, make them a teaser, so I'm saying map this teasering of 50 to those things, and the way we're able to do that 50 is actually a little complicated, but we'll get to it. Just if we're teasering all the elements, where does mutation happen? It happens at the teaser itself. So, teaser, are teasers simple? It just chops by a certain number of characters. If you had a real teaser that you wanted to test, great. How are we going to test this thing? We actually have to have DOM elements again. And we have to mock those DOM elements out in our test the same way we had to pretend to be time and change time. In order to test that this teaser thing is chopping something down, in our test, we have to create a fake DOM like you would and apply it and check. So, instead, we could make the mutation much smaller. And the specific functions we have here, like compose and stuff, you're going to be introduced to those. But the point is I can just walk through this. So, here the teaser's a lot shorter. It's just a slice itself. And it could be more complicated, but here it's just a slice. And so the code that used to say, Take teaser, map it all over the p, it says, Map this new thing. It's a function, which says, Take the text, teaser it and then set it and do it on all the paragraphs. And if the specific functions we're using seem out of the blue, you'll be seeing them more. The point is the mutation on one is happening on teaser and the mutation on the other is happening only on setText. Teaser doesn't go out and change the DOM. It's just given strings. So, the first thing is we recognize we're separating inputs from just random other things like arguments from environment. And here we're separating mutation from calculation. There are functions that all they do is calculate. And there're function that change things about the environment. If you have some functional programming experience, this is probably a little slow for you, but it's just something I wanted to mention about separation.
Recognize Pure Function
And the next bit is recognizing these types of functions that are really useful, like the daysInMonth as a pure function--a function that doesn't change anything outside of itself and reacts the same way to the same inputs. They're called pure, and what does it make them? Well, we've just been beating into the ground it makes them more testable. As Brian mentioned, it makes them more portable in the case that they don't actually depend on some browser thing. You could use them on the client and the server. It makes them memoizable. And memoization is a technique that once you run a function one time with a certain input, a little thing watches over and says, Okay, I'll remember that next time someone tries to call this function with that input, I won't even ask the function, I'll just give it the input. And like in Haskell and other places, and I guess theoretically in JavaScript too, you could memoize these things without having to think about it. So, in the cases where you want to make that space versus time tradeoff in your programs, there's nothing you have to change. Your function's already pure. And, finally, parallelizable, the tongue twister word. So, because the things don't change anything, it doesn't matter which ones you're running at what time. If you have different processors, you can run as many as you want. They're not going to step on each other's toes. So, do we really understand what purity means? Let's plan pure or impure. I guess we have an advantage here with people that are present because we can shout it out. So, here's a function--getQueryVariable(variable). You give it a variable, and the idea is if I was in a URL---if I was in a page like items questions page = 3, and I say, Give me the page, like the query string parameter, it should give me 3. So, I give it the variable. It checks, and it gives me---pulls it out of the URL and gives it to me. What's your guess? Pure is good, by the way, so I'll put thumbs up for pure, kill 'em for impure. You feel thumbs down. Why do you feel thumbs down about this one? It's looking at things it shouldn't look at. You're not pure. Next. Here's one--random numbers. This thing generates random numbers. Who know what comes out of that? We give random numbers like these seed values to kick it off, and it gives you a random number. Kind of complicated in there. What do you think about this one. You're happy about it. Is anyone else happy? You're not happy about it? A lot of happiness. It's making us all joyful. It's pure because, although it's doing complicated things, it's doing them in a well-defined way. It's just like some number crunching, and you give it the two numbers. And it's kind of crazy, as a side note, that this thing actually generates fairly statistically spread out numbers based on what you put into it. I was playing, nudging them by one or two, and I was getting drastically different numbers. It's a bizarre algorithm. So, pure or impure, the final round. ChattyAdd--it adds numbers, but you kind of want to know what's going on because you're balancing a checkbook or something, So, it's telling you what just happened so you know what's going on. So, is this good? You've got to feel like Caesar here. I want to see more thumbs. So, does this thing live or should it die? Oh, a live. I would feel like if I think about, it's what I said, a really pure function, one that we can really count on, it could be parallelizable, I got it that time, and it doesn't matter when they're happening, the result will look the same from the outside world. And if this thing's logging, and if you ran it in parallel, you wouldn't know which order the logs might come in, so it's not pure. It's changing something. The log is something, it's out there, it's outside the function, and it just went out there and did it. Although, wouldn't it matter what the console actually does? You're assuming you know that console is an impure function. That's a good point. It's a dependency injection. Maybe the takeaway would be that if you compose pure functions, it stays pure. But if one function ever relies on another, it'll become impure as its argument was impure. So, purity begets purity. So, would this function actually be pure? What you're arguing is it's the input that makes it impure. Yeah, you're right. Because you're assuming console is impure. You're right. I guess I assumed it was in a browser and it passed through the console, but it doesn't have to be the case. It could be something that just throws it away. That would be very impure. I don't necessarily know what console.log does. Yeah, exactly. It could be something innocent as a flower.
Separate Functions from Rules
The next one is we separated from the environment, we separated mutation from calculation, we could even separate a function from rules. And it's useful to think about this. I mean, ultimately the functions we write are defined by rules in programming. But if we step back and think of then not as the rules inside of them, all the things it does, but just--- here's the thing, it's just passing things through and it sends this point to this point. So, thinking of it more as a noun. You can hold them in your hands. Just like you can add numbers, you can do things to functions. They're nouns. They're not just there to do, I guess---in English, there's run and there's running, you've got a gerund. But this is like the gerund way of thinking about a function. So, functions are nouns. So, one way we could think about a function, this is part of MD5 and JavaScript, is, well, here's what it is. It does this stuff, and there're these weird variables and number and stuff. Or you could think someone's brought a huge platter to you of all the finite strings. It must be a big platter. And associated with each string is just like this tag of what MD5s do. So, the function is just the platter of all the things that it does. So, when we think about it, what it does inside, like the way it operates, is thinking intensionally. Otherwise, it's extensionally. So, if think about it extensionally, we can now make operations on functions and reason about what they do without caring as much what's happening inside. Just to keep it kind of mathy in case like all this is kind of boring, if people don't know this bit, I wanted to say that so set theoretically, a function is a single-valued collection of pairs. So, as I said, there's a platter, and there's a bunch of strings and a little tag on each string that says, Here's my MD5. That's the pairing going on. So, if you had a function that goes like this, the one on the left is a function. I don't know everything---well, I guess, assuming it's good in the dot, dot, dot, the part that we've shown is not making it not a function. But the bit on the right is certainly not a function when you relate---so, when you relate 1 and D, we're cool. That would mean---relating them means you could think it's drawn diagrammatically as 1 goes over to D. I relate 2 and B, we're cool. That relates 2 and C, but we're not cool, because 2 is already taken. You need to be able to decide where it goes. But as a set, it just means it's single value, that the first component of the pair can never have two things that go with it. So, thinking in terms of these diagrams now, oh, so there's another terminology, if you're not familiar with it. When we're looking at, wait for the slide, when we're looking at the numbers, all the numbers or values of any kind that you see in the pairs on the left-hand side is called the domain, and all the ones on the right-hand side is the range or the co-domain depending. The range can be bigger. So, with that---just knowing those words, the domain is the things that are eligible to be put into the function, and the range are the things that it'll ever give you. Like, some functions like squaring a number will never give you a negative number. It's just not possible, so that's not on the range. Knowing that, let's pull it back into JS. It was cool to think about it mathematically. It was a nice place to be, a little quiet. But, the functions in math were taking one value and giving one value. But that's a pain. I have functions that take a couple, you know, in programming. There's a technique where we can separate the arity from the function so that we can write functions that take a lot of things but only give them arguments one at a time, and the thing knows how to deal with it. You're like, here's one argument, you take three, and it'll say, Okay, I'm waiting, when you give me the next ones, I'm good. It can just remember that. And before getting into how that happens, let's see one way that separating can remove names. Because we're saying once we separate and recognize, we're removing spurious names that don't have to be there. So, here's a thing that works with some objects. At the top, we have this function that's going to help us out. Real simple. You give it a property name and an object, and it just looks it up in the object. It's like using dot. But when we make a function for it, as opposed to using a language construct--- you know, it's funny, you think, I want a language that lets me do a lot of things. This is great. Look at all the stuff it gives me. But when you do it with the language, you're not able to talk about it. You're not able to compose it together. So, it's like, cool that language does it, but then not cool that we can control how it happens. So, when the language gives me dot, I can't do what you're about to see at the bottom of the page. When I have a function called get, we can use it. So, assume we have this get thing that gets a property out of an object, and assume we just have a list of people. I guess it came back from some API or something. It's JSON, it's an array of objects that have a name. And I make a function called getPersonName. I give it a person, and it just, I mean, it's super basic, and it pulls the name out. So, then if we had a bunch of people and our task was to get their names, we would map this function, getPersonName, over people, and we'd have their names. Another way is if we had magic, and that's more fun. So, the magic---okay, we have get. We've got to put two pieces together. At the top, we have get. It takes two arguments. It takes the property and the object. And down here, I'm getting those names again through the array just by saying map over the people and get name. But you're like, wait, wait, wait, get takes two. We just gave it one. How's this happening? Well, that's magic. The second one is, of course, map will go over and for each person, it'll send it into this thing, this Frankenstein function, which is get. It's like a weird little copy of get that knows name is baked into it, but it's waiting for the object. So, if we had this kind of magic, and if we think about it that way, we don't have to write a function called getPersonName. It's shorter. You just say get('name'). And, in fact, there's a little trick about the order I put that in. I say getPropertyObject. I didn't say getObjectProperty. Because if I say getObjectProperty, I couldn't bake in the name because the first thing it's looking for is the object. And oftentimes when we're mapping, we don't know what the objects are going to be. So, depending on the order of the arguments, it's going to work better with this magical technique called currying that's going to allow us to put things in one at a time. And, in fact, Brian had a popular talk about You're Doing It Wrong, and it is very much that because people don't use the style thinking about functions as little nouns and being able to compose them and do stuff to them and curry them, most JavaScript libraries the things are in the wrong order so you can't do this stuff. So, there's a set of libraries that we're going to show you that are going to work better for this. There's a question from Valentine in chat. Would ES6 flat operator affect in any way the arity approach or magic on functions? We're going to look into one implementation of currying, and we'll see whether this implementation I'm going to show you is effective. I can't think off the top whether it would or it wouldn't. But when we see the example implementation. Do you have a feeling about that, Brian? Yeah, I can say, I mean like, there's multiple implementations, and already this one creates unnecessary closures, and functions take properties, so you can actually bake the arguments into the function and return a new function, which would get rid of the closure. So, yeah, there're a bunch of different implementations you could do for currying, but let's go for the naïve, simple approach right now and try to make it better with everybody's help. Okay, so we talked about the magic. Oh boy, and I know it said magical spells were nice. You know, they're always a little bit garbled. And the question remains whether the curry---what's more complicated here, this crazy thing or curry? And the fact that it looks so complicated is really just an artifact of we're working in JavaScript. Other languages like Haskell, cough-cough, it just does it. You don't have to think about it. But here, it's alright. We can do it. I'll just go over this really quickly, and we can think about how this one is implemented. And like Brian said, there may be other approaches that aren't as unwieldy, but, I don't know, this one seemed kind of bulletproof in some ways. So, okay cool. Curry, what's it doing? It's taking a function and, ultimately, what's it returning? Well, there're really two things it could be doing? Line 3---well, it's returning another function on line 2. So, you give it a function, it gives another function like a stunt double or something. It's like this other function that's going to, as needed, pretend to be your first one. But it also kind of remembers things for it. So, in one case, here's a function. What does it do? It checks to see whether the arguments given to it are more or less than what the original function expected. So, it's kind of messed up in JavaScript. I guess messed up is cool in this case. You could ask, How many arguments was the function declared to accept? Of course, in JavaScript, you have another pseudo-array called arguments, and you can take more. But if you at least declare your function as taking two, there'll be a .length on it, and it'll say, Well, I guess I stated that I take two. So, this thing says, I'm a new function. If I was given fewer arguments than the original function given to me, like wanted, I'm going to keep note of what was given to me and then return another function, which is sort of like me but it's just waiting for the next ones. If you're given everything that you ever needed--the original ever needed, it says, Well, just pass it along. I have no idea what the original did. Give it to the original. So, it's just that way of delaying what happens as needed. So, you feel pretty happy or at least like you've got a feel for it? We're going to use it. So, even if you don't get the nuances of how it was written, you'll see how it's used. It's easy. So easy that we're going to go to our next thing. But before we do, let's see---I wanted to say, just to reiterate. So, we had our get thing, right? And this is the same get function we had before on the top. You give it a property, you give it an object. But instead of saying function, get, take this stuff, you say, Get, you're the curried form of the function what you used to be, basically all the stuff inside. And when you say curry around it, that's going to return you a new function, and you're good. You can use it in the magical way. So, you can say, names = people---oops, people(get('name')). You just gave it one thing and it's going to take 2.
Currying Exercise
Now, we get to use it. I think this is the first real exercise. Jsbin.com/romun, which I conveniently… Something I should mention is that lodash gives you a curry that I've been using day to day. I don't know how they do it, but if you just require lodash, you can do _.curry any function, just if you're worried about the actual implementation, there're others out there, and they're baked into your popular libraries. So does Ramda. Ramda has one, right? Yeah, you don't have to go into the wilderness and take some sticks and tie them together and make curry. It's there for you. In libraries like this one, which I will wholeheartedly endorse, I really enjoy it, we're looking for different libraries to use that seem to do the stuff that underscore and lodash and stuff want to do but does it right. And this Ramda.js is the thing. It's really tested and meticulously documented. I love to see a lot of libraries that work like this. And there'll be a link to the GitHub. I'm mean it's up here, too, at the end. So, this next exercise I'm trying to see if it includes anything. Oh, yeah, it includes a bunch of stuff, so why's lodash in there? Just as I was badmouthing it, it's in there. Well, it'll still work. Okay, so our currying example. Same format. At the bottom, we have background code. I think if I run this one, there're some things that are logged out just because it's part of the narrative. Like around the comments, there's some logging, and this is the result. But then we have the functions are failing. So, the goal of this is to read through and fix the test so that they work and ask questions as you go. I should say that _.identity is just a stub function. It's just better than writing undefined if you see that. Yeah, we put identity to mean the wrong thing, replace this. If you get a Ramda that's not defined, just refresh. I don't know what's up with JS Bin with that. We didn't really require JS Bin or anything, we just pulled in a bunch of stuff. So, sometimes there's _____. That's happened to me three times during the course of doing some of these examples over the last weeks, so I don't know if it's common or not. Nobody knows how to turn on the line numbers in the JavaScript pane, do they? I did. I just pasted it in though. I just copied and pasted it to my _____. Just paste that in your actual JavaScript console, and it'll change your settings in local storage, I think. And then refresh the JS Bin. Oh, paste this back in the JS Bin is what you're saying? Paste that into your console or whatever, and it'll set the setting in local storage and then refresh. Also, if we want to talk about lines, I can just look at it right here for a second. I got a Ramda that's not defined, but then I just ran it again, and it worked. So, you might not have to fully refresh. I guess it's more common than I thought. If you're not seeing anything in the bin, you go to the upper hand and this appears, Edit. So, confusion about the assignment. Does anybody want me to look over their shoulder or have a question in here?
Currying Exercise Hints
Okay, some guidance about how it works. At the beginning of this JS Bin, it gives an example of using---there's this function called multiply. It just works like you'd think. It takes A and B and it multiplies them together. But, luckily, multiply has been curried behind the scenes. The library curried it so that we are able to make a new---so, the first example that was already done says, How do we make a whole new function without having to write a function that takes whatever that would double a number using multiply? So, multiply takes two things. We want to make a new function that doubles something and uses multiply. Because multiply is curried, we can give it one of its arguments. Like, here you go, enjoy, sail away and be a new function that does things. So, we said on---thank you for the line numbering tip, on line 20, well, actually line 18, we make the thing. We just say multiply, give it one of its arguments. It originally expected two. We gave it one, and now it's waiting for the next one. So, now it's acting like---double is a function that just takes one thing. And so when we say on 20, console.log(double(13)), we see 26. So, it worked that way. Now, the next challenge is to make one that splits words. So, there's a function called split. I guess it was done with Mississippi. Split takes two arguments, much like multiply does if you think about it. It takes two arguments. The first is, What do I split around? If I give it a space, it looks in a space b and says, Oh, there's a space. I'm going to break this into an array with a and b. If I'd given it a b c, it's like I'll break it into three things. Anytime I see a space, in fact, it's really dumb. If I'd given it two spaces, it'd be like, There's an a, a space, nothing, another space, it just breaks around spaces. So, we would like to make a function using curry and split that does what I just wrote here--it splits around spaces. And remember that just like in multiply, we can feed in one argument at a time. In fact, I wonder if split is already curried. It probably is. Oh, wow! That makes it kind of a cheat, doesn't it? So, hopefully, it's not a splitter at this point. We could just say because split and its kind are curried because they came from a nice library that automatically curries, I mean I could write it maybe explicitly, but I could say split(' ') and only give it one argument. Let's see if it passes. Yeah, now we're on a different thing, the next one. So, the whole point of this, even if you don't understand all the mechanisms of currying, is if you have a function which has been curried, you can feel free to give it only some of its arguments, and you'll get a new function, like we did here when I created this words function on line 36. I just gave split one thing, and it's ready to do the rest of the thing it would do. Does that clear up? Let's see what we have on chat. One gotcha on challenge 3 is you need to use a number lower than -1 in your partial application. Luckily, if you're looking for a really low number, JavaScript has -infinity, which is pretty low. So, you're not seeing things getting completed. So that Mississippi works, and then as you complete it, you don't get the error. Correct, that's what I'm running into. You might want to say something about that. You'll see that errors just deplete. You won't see a success message if you pass it. The homemade testing suite isn't so good. Should I give you a hint for 3? On you're going to want to use Reduce and something lower than -1. What's the actual lowest number? It's like number dot. It should be just -infinity. You just actually do a literal negative and the word infinity? Yeah. That's great. When do we find the arguments to reduce? Is there help someplace on this library, or do we have to go to… So, fold left is reduce. In the class, does everybody kind of know what currying is? We're kind of hitting everybody from all different levels, so if you're bored, it's going to get harder and harder and harder throughout, so definitely ask questions. If there're people online here that are still curious about reduce, I'll go through another example. I'll type some stuff in the console you can see and show it in action and make it a little bigger. Ramda doesn't load sometimes because we didn't require an address. We just used a script tag and maybe, I don't know, you'd think it would load, but if you refresh, it'll eventually work. And I think if you just press Run anyway, it will be there despite the warning. Yeah that should work.-- We can talk about that for a second if that's confusing.-- So, Since Map is a function now that takes a list, we're just going to give it the list when we call it. Oh, okay. And it's pretty crazy, so you're kind of pushing off any data to the caller, and the beauty in that in that, is you don't have to call anything lists. You just-- okay (inaudible converstation) So the way that reduce is going is it's giving each an accumulator and each argument to it and it's building it up. So-- whatever it returns it's going to keep as the accumulator for the next duration. Hey, Joe, do you think we could walk through at least challenges 1 and 2 and then maybe give them a little bit more time to do 3 or something? Yeah, sounds good. Or we could just walk through all three. Sure, okay.
Currying Exercise Solutions 1 and 2
So, we saw how to make a double function. So, here's the way that I solved 1. So, words---we have our split, so what does the challenge say? It says, Make a function called "words" which returns a list of words in a string. Use only the split function and currying. Now, I guess what's slightly confusing is the currying is not something you have to go out and do. The currying is baked into split and baked into a lot of these functions so that, if you know a function has been curried, then you can say to yourself, Okay, the docs say this function takes two, three, four, however many arguments, but I'm free to give it one argument, two, whatever at a time. You can give it fewer arguments, and it'll behave---and it'll be waiting for its next ones. So, split takes two arguments. What do I look for in the string to split around? And then give me the string, and I'll do my thing. So, I said words. I called split. All I told it was, Here's what you split around. And because it's curried, it says, Okay, I can work with that. I'm ready, give me a string. I'm a new function. I'm just waiting for a string. I know what to split around now. So, I say words = _.split with one argument. So, the beauty, I guess, the reason we included these exercises is to say I named it here, I called it words. But sometimes you don't have to make new functions to do things. You don't have to name them. You just use the ones you have, but they're written in a way that they can kind of be partially filled in. So, that's the first challenge one. So, now, the split, that's not the JavaScript split, right? That's actually in the Ramda library. Yeah, it is. You could make the curried version of the JavaScript split if you want to, but they've already done it. That's part of why I think it's a little confusing is knowing, okay, this is actually something predefined for us. And _., although it's not underscore, it's the Ramda, which is currently mapped to ugly underscore variable, and then on that Ramda, we're calling the split function, which has already been curried for us. Correct. So, I guess some clues about our vague intentions here. When you see underscores in the code, like _.something, it's just been supplied to you, and it's not from the underscore library. It's just that we wired things up---let's see if it's in our setup code at the top. If it's not, then it's in our HTML. Right up here at the top. Line 1 says it. So, when you include Ramda---I think Ramda works with AMD modules and stuff, but I think it also exposes this global called Ramda. So, I just assigned Ramda to underscore because I wouldn't want to type Ramda.split, it's kind of long. Although it might have been less confusing, so, yeah. Things that have underscore before them are from Ramda. Okay, so we did the split thing. That was #1. Challenge 2--were there questions? Create a function to triple every number in a list using only _.multiply and _.map. Well, we saw one way that multiply was used with currying. We made a double function by passing it two so that it's still waiting for another argument, but it's going to multiply it by two. Well, we want to triple the things in a list. So, the first thing that comes to my mind is I'm going to modify the double thing and make a triple thing where I just pass it three. Is there a question? Raymond W. had a question about whether we'll be talking about what the best word for supplying arguments to functions is to support efficient currying. That's a good thing to talk about. Generally, you pass in---you let the argument be kind of the last thing because if you could fill in some little values, like here we had split, I could've said---no, split takes a string too, like map. No, that's a bad one too. Like reduce. If something can take a function, let it take that function last and let the little scalars be first because, then, you can fill in the scalars, you still have a function. Basically, the data you're operating on---let's say you're getting an API call of information, that's the thing you want to go last so you can partially apply everything out to this point of the thing that you don't have. Like, you have other functions available, so you can combine them. And you have arguments like split space, I have space so I can give that the split. But I don't have the string yet. So, think about the thing that's going to go into your function as the thing you don't have, like the data flowing through your app. Right, so in our words function, it was nice that we could be like, I want to split on space. Space is a little thing. I have it in hand. But a string--I'm not likely to know what that string is. I'm splitting some kind of data. It's given to me. It's something I don't know. So, let the things you don't know be last so you can fill in the things that you do. And then the remaining function is waiting for the thing you don't know about. So, the currying is supplying it from the first argument forward. So, if I give it two arguments, it'll fill in those first two, and then the third one is unfound. Yeah, it keeps being empty at the end---waiting at the end. One thing that's really cool about that is if I want to make a function that replaces dashes for slashes, I can give a replace my dash,slash, and then the last argument would be string, because I don't have my string yet. So, I can just make a new function out of some arguments. But if I had to have my string, now I can't make that function. I actually have to get a string called dot on it, and that's the difference between your dot syntax and your kind of functional functions here that you can combine. If you don't have your data yet, you can still build them out. But with the dot syntax, you have to have your data so you can call dot on it. It's still functional that way, too, by the way. I should mention that. It's just that it's the difference between dot syntax and not dot syntax. So, thinking about our triple list thing again, well, let's think about it. It's going to act on a list, that's one thing. But there's going to be tripling. So, how would we triple? We'd say multiple(3). It's a function, still waiting for its other argument. You hit it with 3. Now, we want to do it to a list, and we take a list and make a new list element by element with map. So, we want to call map. And what is it that we want to do to each element? Well, that's easy. We want to triple it, and we already have a triple function. So, let's see if it actually passes. I wonder if there's some stuff from earlier. Unexpected semicolon. Runner 62. Under max. I was playing around with some other stuff. Let's get rid of that. I had begun typing that bit. There we go, okay, so that made this one pass. So, why did this happen again to recap? We wanted a thing that'd triple everything in a list. The way that you do things to things in a list and give you a new list is with map. What would you like to do to each thing in that list? We'd like to triple it. So, it's two curries just plowing together. So, if there's some really complicated, gnarly stuff, as soon as you edit this JS Bin, you'll get your own number, and you can just paste that in the chat, and we can look at it together. So, the error is confusing. The way I'm thinking about that, though, is we should think about what split does. So, when you look at the log. So, you see how Mississippi is like m, ss, ss, pp, blank. Split--the first thing you give it is what to split around. So, if I ask it to split around i, if I had this thing, I'm calling it words. I want it to---for every word in the list, I want it to break that word apart. If I split on i and I put Mississippi in, I wouldn't get an array of Mississippi. It should be splitting around spaces. Well, I'm wondering if there's some other reason this is failing or if this is just--. 36 should have a space, not an I in the split. So, when you split around the thing you want to split, yeah, there we go. The trouble Valentine was struggling on challenge 2 because challenge 1 was failing, but he thought because the error message showed up about 1, 2, 3, it was, I don't know, I think it was more a confusion around the error message then the actual example.
Currying Exercise, Challenge 3 Solution
So, we did 1 and 2. And 3-- what does 3 ask us to do? Create a function to find the largest number in a list. And what are we allowed to use? This greater thing, which we just created, which returns the greater of its two inputs. So, it's saying we can do this with currying and one of the list functions, map, filter, or reduce. Well, let's think about that first bit, think that through first. So, we want to find the greatest number in a list. Okay, that kind of rules map out in my mind, because if I'm given a list and I'm operating on each cell and making a new list, I don't want a new list. I want the biggest thing in the list, so there goes map. Filter is like almost there. What if we had a way to filter out the ones that aren't the greatest, and it would give us a list with one thing, and then we could take the beginning of the list---it's kind of complicated. Whereas reduce is pretty good. Reduce is like rolling a snowball. You have this thing that---you have the beginning kernel of snow, and you roll it on the ground and it gets bigger and bigger and accumulates. And what would this snowball be? Reduce is often used to add numbers together. You'd say, I start with 0 and then the numbers in the list, I add them in, and that's my snowball growing. Here, we have a greater thing, and in a way that Brian's going to describe, these things are---maybe this isn't a monoid, maybe it is, max with numbers is a mathematical structure that you're going to identify. When we were saying separate and recognize from before, we're going to start to recognize mathematical structures. And seeing those structures will make you think I should be using a reduce here. Well, in this case, it is a reduce. So, I'm going to walk through what it would do, why I'm about to type what I'm going to type. Reduce takes a function that says, Given what this snowball we've accumulated so far and the next flake it's about to run over and pick up, how do we make the new snowball? And this greater thing, if you'd been rolling over and saying, Every number I see, if I see a new number, and I have the current winning number, we combine them to find which is actually higher. And you keep doing that, so your snowball's really just one number that's getting larger and larger and larger if it finds larger things ahead of it. So, we can reduce---you see how greater works, by the way? Let's check it out. Greater(1,2), greater(2,1). It just gives you the bigger number. So, let's reduce on it. I'm trying to think of the order that they do it. Is it the function and the initial value? So, it'll be in this thing? But wait, if I do this, it's actually going to fail. Can we think of why? We start with 0, and now we're going to start seeing things from the list. Or will it fail? I'm thinking it could be a problem with negative numbers. So, we have two tests. This is to kind of---so it passes the first test, but in the case when we passed in 0 for the second test, 0 is actually bigger than any of the numbers in the list, so it de facto wins. It's like before you even started, it's over. So, that's why we're talking about the -infinity thing. We want to start---if the way we reduce is to find new kings of the hill, new bigger numbers, we want to start with the weakest number to be sure that everyone has a chance. Don't put in an insane contender to begin with, -infinity. There you get your satisfaction at the end. All tests pass. Did we go over challenge 2? To triple every number in a list? Using a map and making a triple and putting them together. I wonder if now's a good time to continue. It's hard to feel where everybody is. Is everyone here good? Thumbs up? That wasn't a lot of thumbs up. Does anybody need a reduce primer or does everybody understand reduce? Because we're going to look at those with monoids. We could use adding numbers to reduce. I'm trying to think of another good example. Concating a list. So, we have concat, don't we. We have push. That's kind of impure though. Our accumulators got a list going already. And it wants to put a new thing into that list. So, let's think of how we'd do that. So, the goal---what is our goal? If we have a list to make another list that's the same, that's kind of a weird goal. Implement map with reduce. Standard tidbit: Reduce is a catamorphism or a fold. So, how's this going to work? We have a list that's coming in. We have a function which we apply to each thing. And we want to build up a new list one at a time with it. So, we'll reduce---oh, I see. I'm going to make, actually, until I figure out how to do it without using a working list, using with arguments. My newmap = function---I'll curry it too. A function that takes what would I like to do to this thing in the list. And inside we'll return reduce. What if I should write it in Vim or something or some other place and then inline it and paste it in here? Let's do that. So, we're going to curry this function that takes this, cool. Full screen it. So, we're reducing with another function, which I think is the composition---I hope this is useful--- with the composition of concat and our thing we would like to do, I think. So, we have something that's coming in. Let's see if it works. And we're starting with an empty list. Or we're starting, actually, with---this looks really iffy. Hey, Joe, I don't want to interrupt you, but I have a couple of questions about what you've done. Actually, let's take the questions and give me time to think about this. Dominic L. was asking, Does reduce when not passed an init value not just start using the first two values in the array? Isn't that how array.prototype.reduce works? You have previous val, next val, index, and array or something, right? If you want to get down to the, or you can answer that, I just want to mention that JavaScript's reduce is more like---it gives you more stuff than the normal other language reduce. It'll pass in indexes and the rest of the array. The main things to focus on is the previous val and current val. That'll get you reduce across any language, the idea of the fold. Mims W. is asking that the starting value that we were providing seemed a little weird. Can we explain a little bit more about how initial values should be used with reduce? So, the one in the exercise or the one we were trying to cobble together? We were implementing map with reduce, and that's pretty clear on how the accumulator gets passed through. The seed is just the very first accumulator. So, the empty list---I feel like following through on that really quickly. I feel like #1 is probably incorrect. How does it look to you? I would write a function instead of compose. What should we call this thing? ConcatList. One of the things I'm going to admit to here, when you first were saying you're going to do this, I thought about using the push on the array because I'm JavaScript, which push---and you're going into all these other functions that you obviously know the library. If you want to keep adding onto an array through this, basically make map with the reduce, I was thinking, Oh, well, the input's an array, and then I'm going to push every element onto it, was my initial thought. But, you're going beyond that because of your knowledge of the library. Also, because even in native JavaScript, there's .concat. What I don't like about push, I like it in many ways, is that if I say 1,2,3.push(4), it returns 4, not the new array. And it modifies the original array. So, it's like this weird changing thing. It doesn't even give you back what it changed. It's really awkward. So, if I was going to try to compose that with other things, it would break the string because it's not giving you back the thing in progress. It just says, You pushed 4. I don't care about 4. I care about this list that's happening. And Concat is pure pushes impure because it mutates D originally.
Currying Exercise, Challenge 3 Solution, Continued
What do we use to get accumulator and our element. You just want to concat. Acc with the function applied to the element, so the changed element because we're mapping. And I've got to return, I guess. So, we'll reduce, and the tab key is broken, List. Wait a second. We've got to get the real list in here. So, reduce takes, oh, yeah, the initial value. That'll do it. I was thinking of currying and not testing what it has to be. Let's join it up and try it out. I could paste it into the other side. I don't have to put it in the console. I could paste it in here. I could have done that all along. Newmap takes its function, add(1), let's cross our fingers. Does anybody have an idea? Live coding before noon is just not… Check out newmap. Make sure newmap is a function. The goal is to explain reduce at a lower level. I think that mostly covers it. I'm looking up underscore, and they give the previous example just kind of doing a sum, sum of an array, and then they actually do an array of array and reduce it down to a single array. Does that kind of make sense? I'm curious, though, with all the currying and all that kind of stuff, what you were talking about, with underscore, it starts the list. And you're saying Ramda and the other ones, you want to start with iterator first, and then the memo, and then the list, so it's just an order… It needs to start with the function you want to do. Yeah, because you don't have a hold of the list when you're building your app. The list isn't there yet. But you do have your function and your seed value. Yeah, we could fix this during a break. We could do a simple reducing sample or move on and fix it. So, we were trying to figure out what does reduce do? And we thought, Oh, this will be kind of easy. We can make one that does some list stuff. But it was a little more complicated. And, hopefully, this will help to explain things. So, we can do some simple reduce stuff too. We've got---here's our newmap. Newmap is just supposed to do what map does, and when I run it over here, it does. So, I say, Add 1 to 1,2,3, and we get 2, 3, 4. So, that's good. What is it doing? How is it implementing map in terms of reduce? The crucial line--this doesn't have the lines enabled, but there're not many--is reduce our concat list starting with an empty list and then working off the input that was passed into the whole thing. So, because it's really just an implementation of map, it takes the function of what it does to each thing in the last and the list it does it to called list. So, the real magic, I guess, is how do you combine the list so far---map always says, How do I combine the things so far with the new thing with the way of combining it? So, the way of combining it is concatList. It takes the stuff so far often called the accumulator and a new thing, like one element that was given to it, the next thing in the original list. And what does it do? It takes the thing so far, and this is just standard JavaScript, and concatenates an array, because concat puts arrays together---I hesitated to use push for the reasons we discussed before because push is impure and because when you push out an array, it doesn't return the array in progress. It modifies some array and gives you what you pushed on. So, we take the accumulator, what we have so far, and we add a new array onto it. It's kind of a cheap little array. It's just an array of one thing. It's the function passed in applied to the new element that's coming through. So, concatList is an example of the way---the type of thing that you give to reduce to tell it how to combine new and old. So, that's the essence of reduce is building up the snowball. So, this is now in this guy, I don't have the chat on the same computer that I have the display on, if someone else could paste it in there or whatever, g-u-v-o. And you can play with that. In a way, I hesitate to spend a whole lot more time on reduce because of the three, it's the one I think I use the least, and Brian has a lot of brain-melting stuff I want to get us to. Does concat maybe not require the---I haven't tried that. So, the standard JavaScript one does not require---if it sees an element that's not an array, it just pushes it on. Somebody asked earlier how to explain reduce in less than 140 characters, or the catamorphism. But the idea of the catamorphism is that you have an accumulator---it's a recursion pattern captured where it passes in an accumulator along with each element in the list, and you can do that---you can do reduce that way, you can do map that way, you can make sorts. You can do all sorts of stuff with catamorphisms. It's kind of like the mother of recursion patterns captured in a higher order function or pattern. So, reduce kind of captures a recursion. And if anybody gets all, Oh, you don't have tail recursion optimization in JavaScript so you can't do this. Well, it's just a pattern. You shouldn't really be doing recursion explicitly these days anyway. There're already captured higher order functions. And Jeremy Gibbons has a great paper called Origami Programming that kind of takes a stab at saying, Stop doing recursion and just bake it into patterns. Generally, it feels like when we're separating and recognizing, the same way you don't want to write loops, raw recursion feels like the functional version of loops. You have things that'll just do it for you. So, let's just do one more right here, reduce(_.add) I think would sum stuff up, but I need to also give it a 0 maybe. So, if I apply this thing, and I don't even have to name these things, 1,2,3, we get 6. So, reduce, first thing, how do I combine stuff. Second thing, where do I start from? Because initially you don't have anything. And the thing you pass in. So, that's the essence of it. Just play with it. Like I said, I don't use it that much. Maybe Brian uses it more than I do. Now, you have two parameter lists. Is that allowed in JavaScript. Is what? You have the reduce with one parameter and then the array in the second parameter list. This is the magic of currying. Curry's coming back. So, I said reduce two things. Reduce takes more than two things, but it's secretly been curried by Ramda, the underscore means it's coming from Ramda, which means when I give it fewer things than it expects, it's giving me back a new function. And to that new unnamed function, I'm passing the array. Hey, Joe, there was a question. Maybe you guys will get to it. It's kind of general. Mims is saying, All this is very academic and cool, but how might we use this for real-world applications. Don't you worry. We're going to have examples. Actually, one point of this is these functions are super pure and academic and stuff. But in part two, The Voyage, they're going to be pulled into the browser, downloading stuff, doing stuff. Our final demo is going to be like an actual search engine that finds YouTube videos and plays them and stuff, so never fear. In any case, if you're making a for loop and starting off with an initial value, there're bits and pieces lying everywhere and explicit imperatives, lines, steps of code. This is a declarative way to just say, I don't care how you do it, just do it. Plus, goto, I like it. It's kind of academic. I like my goto, don't be academic about it. Let's keep on going. Here's a good example of lots of stuff going on. Tell me if I'm talking too much. No, it's good stuff. You'll get your chance to talk too. Currying. We used it, and we also kind of used some of those basic functions too. It's kind of lagging. So, to kind of recap some of the things we did. Rather than having to make a function like this, it says words--I take a string and I do this stuff, and there's a return, and there's a function, and it's just a lot to write. We say words, it's split with space. Same result in either case, shorter. Once again, here's another one. So, you could say, max--reduce with greater, -infinity xs. So, once again, there's a function, and there's a return. I guess that's especially silly. Or just it's that..it's reduced by what's greater starting at the smallest number.
Compose
As if it wasn't academic enough, let's go back to weird noun-function land. Okay, another trick that we do aside from currying---because since we're thinking about functions, we can think about functions, this is just taking points to points and we don't care what happens inside, you can meld functions together. In the same way you can add numbers together, you can meld functions together. Where---I hesitate to walk all the way over there, I'll use my mouse. So, say we have three functions, f-g-h. The melding is written with a little circle. It says, Do f, then do g. What would it mean in terms of the points? And once we get through this, we'll see how to use it like real code and how it makes stuff shorter. So, g◦f is a new function. It's just what they call it. They just named it g◦f. And it's built out of f and g. And the way you find what it does to the points, because remember all we care about is What does it do to the points? We don't need to know what it does inside, how it's implemented. We just kind of follow through and look. We see this first top thing in A. If I were to do f, it would take it to the top thing in B. And if I do g, it brings it down to kind of upper-left territory in C. So, when we merge these functions together and have just one that does one after the other, it should eventually take the top thing in A and get it down to the kind of upper-left in C. And so that's kind of our definition, yup, that's where we're going to send it. So, when we do the same thing, the second one goes to this kind of middle-y one, and that goes to also that same upper-left, and it does. And the bottom goes up here, you just trace it through. So, cool, this is g◦f. Well, there's nothing to stop you from continuing to do this. So, you think of if we want to eventually meld h onto here too, we just keep following through. We say, Where did g◦f take the top thing in A? It took it to kind of this thing over there. And then h would take it up to the top. So, when we go all the way through, we cut out the middleman and go straight to the top. That's just the concept. It's following through where the stuff goes. And you can make a JavaScript thing that just follows it through, and we can start mashing stuff together, and it's good. And aside, Brian's going to do some even more mathematical stuff where we can identify---we zoom out from the code, and we've written things in this kind of pure way, we start seeing similarities that compose in much bigger ways than a lot of things that we're used to. Like, in this future world, everything is contained into somewhat mathematical interface. So, it's clearer when you use a foreign library you've never seen before that it's going to compose and work with yours. It gives you a standard language because you recognize it as category. Either that or you had n conflicting standards, and now you have influenced one. There're different ways to think about it, I guess. So, that's just an aside. So, we learned how to smash things together, and we also---so, we thought, How do we combine new functions, like do one function, do another, push them together, without even giving it a name? But without using this pattern, without recognizing functions as being kind of mathy like this, we tend to do it anyway and do it with more names and do it kind of ad hoc and as the situation needs. So, it uses unnecessary arguments. So, for instance, we use these glue names. Say we had this situation and there's two things going on. Maybe there's this callback. You say on_error, I want to do something. Maybe on_error came from some library or whatever, and when there is a problem, call my thing back. So, there're two things. The message extraction--you're given an error here, right? And you say, Get the message off of the error. That's kind of one concern in a way, pull it off. And another one is like that impure thing of logging. And so now we had to make this whole, I just feel like, function error, error, error. Error is written three times here. That just sounds kind of awkward. So, if we recognize that there's a general way to combine the stuff, we don't need to make weird variables to do it. Just string it through like that. So, let's think about how it would be implemented in this ideal world. Because we have currying, we can think about functions as only taking one argument at a time. So, compose--this is a simplified compose. Compose takes two functions. I wrote a g, f rather than f, g because as we'll see down here, the convention is that it strings things through the functions that are given to compose from the last to the first. And there's another one that's not called compose that goes the other way, and it's escaping me what it's called. But this one happens a lot. So, how would compose work? We want it to mash them together, so we do. We say compose g, f is a new function, which mathematically would be written g◦f, but here it just doesn't have a name. It takes a single value. It sends it into f. It takes what f returned, and it sends it to g. It's really simple, which is good. So, here's an example of a weird little contrived composition. Say we had two functions, one was called properNoun, the other was called reverse on strings. ProperNoun capitalizes the first letter, and reverse just reverses. So, here's how it---you'd take compose over here, and you'd give it the single value hello as it goes in. It's like we wrote before, it'd be a function, you could pass "another" set of arguments to this thing. I could write take hello. It would send it through properNoun, at which point it would be Hello with a capital, and it would send it through reverse, and it would be reversed, and that's the version that would returned at the end of this whole thing. If you know Unix pipes, it's just the flipped version. It would go right to left instead of left to right because the argument comes in to the right.
Composition Exercise
So, here's our jsbin.com/jevag. We're going to do some composing. This time, I'm going to put it in a different browser so it doesn't know me as the creator, so when I type things, it doesn't change the original.Okay. So, like the other ones, we have this underscore. It's actually Ramda. It's coming from Ramda. We created a little helper, which was get, like we were talking about before that pulls something off an object and is curried. I think Ramda has that built in actually, so we duplicated Ramda. So, it starts with an example of how you could use compose. In fact, if I run it, we'll be able to see some output I hope. Refresh. Oh, there it is. Cool. So, we could make this function that given an array of words---because see we have words in here we split. Actually, you give it a string---I'm looking at this, double checking. You give it a string, it takes out the words, and then it gives you the lengths of the words. So, maybe you wanted to make a histogram of how complicated words are and a grade-level analyzer in a word processor or something. So, here's how you do it with compose. I don't have to make it. I just throw stuff together. So, that's an example. Then it's our turn to take something that an API might have given us back about articles and authors and stuff and get a list of the names inside of the articles. So, once again, we have identity written here. That just means change this out, get compose and map. I feel like if we're going to use the JS Bin and the refresh bin, we should figure that out. It's weird, because it's supposed to be synchronous, and the script tag is just an HTML script tag. It should be doing that first. Yeah, we could add an asynchronous loader thing that gets it already. I've had scripts at the top block the page. I figure we'd have to do that. Yeah, she totally blocked the page, she has been doing some weird stuff. Caspar Milquetoast is my favorite auther.-- Lessons to all of us.
Composition Exercise: Challenge 1 Solution
Here's how #1 works out. So, our challenge is to return a list of author names in articles using get, compose, and map. So, if we look back up where do the authors live. Alright, everybody, we're going to go over the solution to #1 and setup for #2. Here we go. #1, and then we'll have more knowledge for #2. So, we want to pluck out the author names. So, we have an array. What's in the array? Objects. We want to get the author, yes, but their name in particular. So, when I hear something that says, get me a list of blah that you've kind of pulled out of some other blah, it calls to mind map. So, the outermost thing is probably I'm going to map over some stuff. So, what is it that I want to do---how do I want to transform each thing in the array? Well, one thing I could do is get the author themselves out. So, I could say get---or actually I made that get. They may be the same--get('author'). This is not quite there, but let's see what that does. Ramda is not defined is what it does. Try again. You'll want to run it a few times and it will fix itself. Oh, it does? It must be that JS Bin loads and runs these things. Each time you change, maybe that's what's happening. That's not good. Anyway, so here we go, uncaught. So, here's the deal. It wants to find---oh no, that's different. These names equal object object object. We're almost there. We just mapped---instead of getting the right thing, we got too much. We got the whole author, and we just want the author's name. Well, let's go back down. Here it is. So, I'm going to put this on another line so we can kind of see it. We're mapping get('author'). It's true, but we have like a process of things to do, and when you do one thing and feed it to the next thing, that's a composition. Or, we could think of it as a composition. You could do it all kinds of ways. But it's pretty clean this way. Actually, one thing that helped me was to run that over in the console. Put that map, just so you can see the output, because that's the hardest thing to sometimes just imagine what it's doing. Good idea. Names should be available right there on the right too. And then you can apply it to articles. And that helped me a lot. Code speaks better than I do. I like it. Here's what that would do. If all we're doing is mapping get('author'), we get a new array back because the original thing was an array. And it has the authors. So, what would---if we were considering each author in isolation, what would we be doing to that? Now, one thing we could do is map a map. It's kind of inefficient, though. You have to go all the way through, get it down to the author, oh, wait a second, yeah, I guess. But another thing you could do is compose what you're getting out of the things. So, we got an author, cool. And remember compose is right to left. So, the first thing it does is get the author, and we're going to feed that into getting the name of the author because at this point, we have their… When you see the patterns, you know, map(map) versus map(compose) is called loop fusion. So, when you zoom out, you can always and without hesitation when you're working with pure functions convert one into the other and get efficiency settings. It's like when things are very well nailed down, I know what they do, and I have laws that they obey. You could just transform your area to make it faster and simpler. We'll talk about that in functors too. Hopefully, it runs. So, that's challenge 1. We can just assume that's progress. So, why don't we just spend five minutes on 2, and then we just give the solution to 2. Is there a hint you can give on 2 because Mark and I are stuck. Make a Boolean function that says whether a given person wrote any of the articles. I can give a hint. You're going to need a function wrapper around this one. Won't you need a closure. You're going to---it's not going to be just map and compose. You're going to need a function that because of the way it composes, it needs some random argument. There's actually a really good demonstration of that. So, you're going to need to write function, colon, parentheses, return, all that junk, around this one. Scott S. and Michael H. mention they've got some combinators in Ramda that you could use instead of fork, which is kind of cool to know. Challenge 3 is just for the fun of it. It's like how "point-free" can you be? And the idea that you can strip arguments away and not name them. Sometimes it would be counterproductive, but sometimes it's really nice. You can get yourself into some serious problems if you're, like, Must be point free. Point free is just a really good way to know you're on track writing declarative code, you're not making--- you're pretty much on the right functional track if you're writing compose a lot. But don't force yourself to write everything where you're---oh, did we even mention what point free was? Only just now obliquely. Point free is you don't mention arguments at all. You just glue functions and data together without ever receiving arguments. You can write all these point free, and you get a lot of benefits because you know you're just combining things generically without ever needing data, and you don't get tied into names that make your program very specific. It is also a really good indicator that you're on the right track of functional programming, and you're being declarative. You're forced to be declarative if you're writing point-free code, which is---you're just saying, Here's a higher order. Do this. Don't say how to do it. Just do it. So, I get that this gives us a lot of terseness and sexiness, but what about maintainability and scalability across teams with different levels of competence. So, one thing that you're going to gain is you're working at such a---if you go up to maybe the top example of just that composing the author and the name. So, compose, map, size, split. That doesn't say how to do anything. It's saying this function is the composition of these two arguments. And those---just like in SQL or something, somebody can make map way more efficient, compose way more efficient, split more efficient. They could do all sorts of stuff. You haven't said how to do anything. You've just said what needs to happen. We're going to compose these two things. And so lengths has a perfect definition of almost reading what it's supposed to do. It says it will split it on space and map the size. And that's the composition of these things. So, you're going to have a much more maintainable code base because you're forced into a very high level of coding, but you do have a lot of wiggle room in implementation. But, with the highest level of programming, you're going to have to fire the people who can't understand good programming. There're different levels of obscurity though. If you're just using compose and map and stuff, it can read kind of nicely just like you're looking at Unix. Do this, pipe it into this, pipe it into this. Just down the line. There's a presentation maybe the Ramda creators can post. It was a great talk showing do you want your code to look like this? And it was crazy. And he put it through a bunch of compositions, and it was really clean. So, I think it can look better and clearer. But some of it's straining. Like at the bottom, like use fork and whatever. That's kind of weird. It was just as an exercise in thinking about moving variables. Maybe not in a way that you'd want to use all the time on that one. Can I back up and say you shouldn't fire people. I think there's a lot of value to above-level code and a lot of value to different types of programming. This is just a way of programming in a declarative fashion, so if you need that flexibility underneath, you can get it. Challenge 2. Go ahead. So, what you're saying is we write the lengths function, and we let the low-level guys use it. Exactly, like, hey, we write compose to make this way cooler. Or, hey, we'll make this map more efficient. And your application code just reads like a DSL almost, and the implementation code is---as long as your application's at that high level, everything can change.
Composition Exercise: Challenge 2 Solution
So, what's our challenge here? Make a Boolean function that asks whether a given person wrote any of the articles. So, we already in the previous one made one that shows us everyone who wrote articles, so we can use---build on that, I guess would be one to think of it. Where's our first one? Oh, actually, we already defined it. It's called names. So, I guess we could use it here. See up at the top we have the solution to challenge 1 is a function called names. That'll tell us the names of the people who did the stuff, so---or wrote the stuff. So, I think we could say contains, we need to get the array put into the thing. What's a good point-free way to do it? Compose and contains. We could _.compose(_.contains). I'm thinking of writing---let's refactor it. Let's do it like this, function(name) and say return _.compose(_.contains(name), names. There's got to be a way better way just thinking it through. Hey, Joe or Brian, could we get elaboration on what point-free code means. I'll go back to the slide. So, this thing ought to make it pretty. I'm thinking of composition. I don't think there's a way to do this one. Actually, we could take out the article, right, because with compose and return is a function that's waiting for what articles it's expecting. You don't think there's a point free way, you say? No, because name has to go in contains and--. If you tried it---if you go make a combinatory to do this, you're just going to end up with weird, crazy code. You should just write a function at that point. So, let's see before I press run if this seems to make sense--isAuthor, you give it a name. It's going to return a composition. It's going to return a function which does names first on whatever it's given. So, that means---because names is the last thing in compose, that means that's the first thing that new arguments go into in the whole composed result. So, names was expecting this kind of object thing that we had before, which was articles. Let's see if it works, so I'll go over to Run. You're going to have to call it in a weird way because if you're returning a function---isAuthor returns a function, so you have to like close it and reopen it when you call it. I was just passing that on. How do we clean this up? Is there a way? This seems a little bit complicated. I thought when I originally made it, I should have saved what the answer to my _____ was because I was shorter than this. I put it all on one line, and it seemed pretty straightforward to me. It was like, Run this composition. Well, it does use compose and contains. Maybe I'm making a big deal out of it because it's not as point free as I wanted. So, isAuthor, you give it a name, you give it articles. It does a couple of things. It pulls the names out, it sends it into contains with name, and that's it. We're going to see a whole bunch of compose, and it's going to feel really useful throughout the rest of this. So, if you're having trouble right now understanding why or what it does, ask questions, and if you're like, Why is this important and why do I care?, you'll just see a bunch more examples so that should answer that second question. Maybe challenge 2 at least as an answer is not a compelling way, like, yeah, compose. I'm going to use this everywhere. Why don't I just pass one function into another? But it's absolutely useful all the time. In fact, in the live demo we're going to make, it's used a lot and effectively.
Composition Exercise: Challenge 3 Solution
But as far as #2, are we solid on that aside from maybe it's not the best use for compose? No. (laughs) So, I'm trying to think. Do we need an explanation again, like how does isAuthor work? People liked the fork example if you want to go over that. So, we want to take an average, and compose is kind of a pipeline where just using compose it would be hard to take the average. Because the average---you get a list of numbers, and you need two facts about it, like the sum and the length. And you think, Okay, I'll start composing. I'll get my list of numbers. Which one do I want? How about the sum. And then whatever you're going to feed that into, you're kind of stuck because you don't have the length anymore. You have no idea what it was that was fed into the beginning of the compose. So, there's this fork thing that says, fork, you give it two functions, and I think a combining function lastly. So, you say, there's a fork in the road. It's like compose. The values come in. They get into fork, and fork says, I've got two functions. I'm going to call this value that just came in on both the functions and then take the results of what those functions gave back and give it to lastly, this function called lastly that---it's up to you how you write it. So, all these three things are up to you. The two functions that you want to do on the input and how you combine it with lastly. So, knowing that's how fork works, the order it takes is lastly, f, g, and then the mystery x. In fact, it's so much easier to just look at it now that I think of it. Look at what it does. Fork--three functions and a value. It puts that value into both the functions and then sends that into lastly. So, if you have it in your tool belt, this fork thing, you never have to--- I think there're less variables you have to write in the future. You can just apply it. So, what would average look like? Like the very worst way I can imagine, the most procedural way is like I have a counter, I have a loop, I add up, I guess I could reduce, I could use sum, that's a little better. And I do a couple of steps, and I return it. But we could just fork. So, lastly would be---I forget if it divides---we may have to flip it or divide by. There're two different ones to say---the order the arguments come in, is it the numerator or the denominator. But we'll find out or it'll be in the chat. Trying to think. So, one of the arguments would be composing the length with divides. Wouldn't your lastly function be divides if it's the last thing you want to do? And then your two functions after that would be sum and size. And you wouldn't be passing those into divide. Fork does that for you. Divide, length. Or is it size or length. Sum and then size. That's kind of pretty. I guess maybe fork is pretty cool. Let's see if it works. All tests pass. If you go up to fork, the definition, that is the function instance of a functor or an applicative functor, so it does actually really come in handy. You're saying this is a more general name than is used in other places? Yeah, if you think of f and g as a functor with something inside and to get it you have to run the function, then, we'll look at it later, but you run both functions to get the applicative functor, and then you apply that lastly. So, you lift lastly, and you apply it to the first steps to get the value out, and then you apply it to the g to get the value out, and you get… So, most likely, no one has to write this thing. It's part of the monadic sort of applicative stuff you're going to do later with The Journey. I don't know if I'm going to cover function instances of applicatives, but that's it. Well, why don't we pass the value x in for fork? Because fork is curried, so it doesn't require everything to be---I think, let's see. So, like in the test, we say assertEqual 3 to average of something. That average as it stands is forked of this stuff, but it's curried, and it hasn't been given all the arguments it needs yet. So it's a new function waiting to get it. That's why we didn't have to explicitly---yeah, exactly. I'm assuming everyone is reading the chats as well as I am, so I'm like responding to a ghost. So, those are the three, and those are their answers.
Point-free
Well, one example of point free--- takes it a sec for the arrow keys to work, is this has points. The point---points mean arguments. So, I don't know why they call it point free. Maybe in the analogy of when I was saying functions viewed as nouns or whatever just take points to points, maybe that's what it was. Or maybe it's something like---hmm, I don't know. But you could think that point free means argument free. Making a new function without having to say function, taking an argument x, w, z, or in this case, taking the function error. Let me find. These are all point free. This has infinity. But, still, I don't have to declare a function. It's really like the data that's going in. That list is your point, you know? I was looking for---there's another one with the error. Here we go. So, this is that same one without the point. We didn't have to make a fuss---so it used to be on_error, call a function that takes a point or an argument named error, and it passes it in. And instead of having to make a function that takes a name to argument, we can substitute that with a composition, which creates a function. It just doesn't have a name, and the argument doesn't have a name. We didn't mention the argument. So, the slides are in an awkward order, but we could code it in JS Bin. But the point is you know you're point free when you're not saying function, and then naming arguments and then using those arguments inside the function. When you have a pallet of tools like compose that you can put other stuff together with. So, you make a couple of basic functions like deep down at the _____ often in a library like Ramda, and you can just push them together and not name it, and you're doing it. Or like in our average thing, when I said fork. I didn't use any variables there. Let's look at that. This is totally point free. So, check it out. Fork--I defined a thing called average, and there are no variables. I just say divide some size fork. It's like snapping Legos together. So, that's how I define point free and know when you're using it. Oh, I see, just the fact that we're also doing currying on top of the composition? Because fork takes four up here, but we only gave it three down there. Is that the confusing part? No, you say that, if, I understand what fork is doing. I don't know that I have enough depth of understanding to articulate what I find confusing about it. Fork isn't point free. Average was built in a point-free way. Fork totally uses the points lastly, f, g,… I think that's what I was trying to say. If I was saying fork was, I was being sloppy. Fork is not. And since you have another minute or two, it might be fun to define two stupid composes to kind of show---maybe do the phone number thing where we replace the dashes for---you can make a place helper that's just curried and replace the dashes with the slashes or something. Maybe you shouldn't. I don't know. How much lecture do you have left? I have a couple of slides. Maybe we could check out the slides and see what kind of time we have and how hungry we are.
The Silence: Review
When the stuff is really pure, often loops are reduce, filter, or map. If things aren't pure, you know you have to worry about what it's doing. You can't just always use it so happily. In fact, there's another list thing, each, but that's kind of the impure one that shouldn't be in the triangle. Like, do something stateful to each thing that you see, rather than map, which is change it and pass it through. Change it purely. So, when you work with pure functions, these are probably most of the loop things that you're going to see. And, in fact, we like to say, if you write loops, you get an F. No loops! So, kind of reviewing the separations. In general, phase 1 was to prepare you to see---it's kind of like the basics. The type of stuff that's going to feed into what Brian shows you to write really cool stuff. So, the basics are keep the function and arguments explicit. Separate input from environment so you know exactly what's going into the function. Also, the inputs can be provided over time. So, separate a function from its arity. Make it be whatever arity it needs to be so that we could just throw stuff together and compose or like split like you saw. Don't be modifying outside things. Or, when you do, keep it very localized so there's like one place that does it, like in the teasers. The teaser function was both trying to figure out how to shorten something and updating the DOM. It's better to have something like, Yup, all I do is update the DOM. And I'm composed---in fact, a way to think about it is you do your pure stuff, and it's sandwiched in between the impure stuff. So, your pure stuff in the teasers was calculating, okay, I've got a string of words. What's a way to shorten it so it's an appealing thing someone wants to click into and it's not overwhelming. And it's sandwiched between reading the things in the DOM, like seeing that they're there, finding them and putting them in, and on the other side updating the things in the DOM. So, if most of your program is composing pure, pure, pure, and only at the end do you ever sandwich it, then the things that are pure are free to combine a lot more easily and compose. So, between the buns of the sandwich, you can rearrange all the layers nicely and freely. Like, you could throw log, a log statement in between in your compose chain and see how things are changing. Even less impure just as a debugging technique. And then try to compose without using glue variables, like we saw with the error thing. And if you do those things, you've omitted a lot of needless words, and your code is ready to enter from The Silence to The Voyage that Brian's going to take you on. So, I wonder if there's anything else we want to cover. Do you want to write some more compositions or just take questions? People need to know this before we go into the second part, or you guys are just going to be like We hate you! I did have a question on the challenge 2. I actually wrote it originally without the compose. I just did a return contains, and then I passed in as the second argument the names. So, in other words, I didn't do the compose, so what's good or bad? It's probably better. You probably did a better implementation. I think it's a good example to be, like, compose works here, but if you have to put arguments in weird spaces, just write a function and just use the normal stuff. But, if we---yeah, I think we should write an extra couple of composes to show---I mean, one of the benefits of compose is you don't have to name anything, so there're no ties to this specific implementation. We're just saying glue this function with this function. And you're kind of, without thinking about it, writing this generic code that's building up little functions into bigger functions. If you---the second you start to write function, and you start doing things in there, you're writing---you're telling JavaScript how to evaluate, do this first, then do this, and it can't be changed underneath. You also end up writing---making all these references to names and having to name things. And you might be doing things that aren't other functions that you're building up new functions from. You might just add one out of nowhere instead of using our add function. So, what it's doing is kind of forcing a higher level declarative discipline, and if you can't get it, you might write mutable code. Don't worry about it. Do you worry about performance then? Like, are you getting too many functions, and does that as opposed to sometimes just doing it in the straightforward JavaScript way? It's funny to think about, like to really think about performance and what affects it. In a web application, it's so probably not CPU-bound on your JavaScript. There're huge network calls, like an eternity compared to calling an extra function or choices like caching that went wrong or waiting for the server. I think it's good to think about it. For a minor increase in efficiency, I would not want to make a major increase in the complexity of my code assuming that I found a nice way to compose where it's clean better than example 2. Should we make some more compositions? Maybe we could mess with those authors. What could we do to this stuff aside from just plucking things out? We could look to Scott and Michael for some cool Ramda functions to play with. Maybe you could say take one, to take one of the articles and compose that with take one article and then get started. I don't know, let's see, so take one article, so get the first title? Show currying and composition working together. I don't have to do var, I can just do compose. Actually, I'll do it the other way around. So, we want to get the title. So, this whole function is going to get the first title in the thing. I should have actually done it on articles. But it's actually still an array. So, get the head. Drop that in the… Or like a first instead of a take? Take one head is the same function. So, that's the same. Head still seems to be like it's an array with one thing in it. No, it's an object. Alternatively, I don't know if this is such a seller because it would be like articles going through functions, we can start to---I'm going to talk about in part 2, we can start to compose contexts, like what if there was none. So, if you try to get the head and there's nothing there, if we're composing functions, we're able to compose behaviors like null checks and stuff. So, that'll get pretty wild. If you do this bottom one, you're locked into the---you're stuck, you're done. You have to wrap that in an if not null, if articles first, get title. Good point. I can't say how many times I've _____ something's null _____. It would've been better to stick something in between, you'd be in a different context, which we'll show you. So, we're starting to do that. But one of the benefits right here is you're completely declarative. You don't say how anything can happen. So, I guess the bottom one is kind of declarative. But I think it's important---I think the biggest thing is what you said, this composes, and it composes in a way that the pure code can be lifted into a container like you're going to explain in The Voyage. And the container can do null checks, so your code doesn't have to care. It's like, I'm pretending everything's perfect, but I'm in this container that insulates me from nulls and stuff that can happen and does other things. Joe, there're a couple of questions from chat. One was whether currying is implemented in underscore or lodash or if they need to use Ramda. I think curry's in both of them. That would be my gut instinct. It's a pretty standard thing. But the thing is, say that you choose to use underscore. It gives you curry, but the other functions it gives you don't have their arguments in a convenient order to be curried. But you get the curry.
Questions
There's another question on what the best way to go about debugging these JS Bin examples is, whether there's an easy way to step through them or something. JS Bin has been a little bit confusing both in order it loads things and evaluates them. So, if I drop a debugger in these things, I could try. I could use JavaScript debugger I suppose. You know what you should do, though, real quick is show them the log and just drop that in the middle of any compose. Sure, I need to define it. Some of the built-in browser objects can't be composed for some reason when you try and treat them as real objects, they say Illegal invocation. And so we have to make a wrapper for it. Log = function(x). We say console.log(x), and we say return(x). That way, it can be composed. If you put it in a composition chain, the last thing it does is return whatever was given to it. So, we could say in here---actually, it's kind of cool. Let's put it in our composition. I think you might need to run it. In that case, I better copy this. Oh, it worked. So, there's that guy, get('title'). Can we put a log in there in between and head? You need a dot on head. So, now it prints out Everything Sucks, and it also printed out the object because it logged it on its way through. You might---one thing I use with compose a lot is if you define that first title and don't call it right away, you can compose that with something else, right? Help isn't working. So, you're saying call the first title? With the log and the head. So, let's say firstTitle and then, what's a good string function. Like uppercase? Yeah, or something. How about, I wonder if head will work. Probably, because the head of a string is possibly like a list. It's firstLetter. The first letter of the first title. That's what it's called. You need an underscore. So, what you can do there, though, is define that as firstLetter, so the first title on head is firstLetter. And then it fails to work after it evaluates. This is a bad example. What I'm trying to get at is you're building a grammar of functions that you're naming. So, I can compose first title and maybe make HTML out of it, like make the header. So, I'm going to grab the first title, and then I'm going to compose that with header and make that draw page. So, draw page is just the composition of first title and make header or whatever, make H1. And the whole point of that is you end up with this English that you're reading as your program. So, if you did it on the left side, it'd be a lot clearer to be able to make your grammar and a couple of functions and then use your grammar when you actually run your app. It's like you're programming with DSLs without knowing it. Do you have questions about that? I think the biggest thing is you also have grammar with dots and looking up arrays and stuff, but the real thing comes with taking these functions and putting them in all five or six types of containers _____, which will become really cool and clear. But if you see any of my controllers in a standard app, I'll bring one up, it's like this function, that function, that function, that function. They're all just specifically named, like this is going to do this, this is going to do that, and it's going to do that. And then at the bottom, you've just got the composition of maybe get from db and put on page and ask user what they want. And you're like, Oh, I see, it just reads right to left. I don't know much Hebrew, but… So, I feel like it pushes you into this really high-level style where you're just writing what should happen, and it keeps you on the same level of abstraction pretty easily, because you want your sentence to be, like, do this, then that, then that. Okay, those are the three things that make my page. And then above it---and we'll see in the demo app we'll see that pretty clearly. Someone once told me in pivotal labs, they use test-driven development not just because they worry about the correctness but because it amplifies the pain you feel by doing certain things wrong if you're doing unit tests. So, doing this style may amplify a way that would be less beneficial, amplify the pain of doing it that way. It's a good design technique they say for---a good API design technique if you do TDD.
The Voyage
Reviewing Composition
Let me just finish up part 1 here with a quick last question that happened over lunch. Hi, Manuel. And the point is that you might want to use this when you're just kind of building up this function. So, over here we have our application that's going to first get an arrayByDashes. What does that mean? It's going to split by dashes. And then we're going to map the firstUpper. Well, what is the firstUpper? The firstUpper is toUpper and grabbing the first character. Or we could be way more efficient and grab the first character and do toUpper. The point is this should be---this should read like a book. So, the next thing is how about instead of app, we'll make this getFirstUpperOfDashes. And then our app is now this getFirstUpperOfDashes, oh, it's going to try to autocomplete. The composition of that and some other function, maybe take(1) or take(2). Take(1) instead. So, at that point, what we're doing here is building up a function from other functions. There's no data going in. It's just like a machine that you kind of drop a coin in. And when you drop the coin in, the coin being this argument that goes into our app over here in the collar. Over here, it's just functions being combined with other functions--composability. And we're combining functions with data, and we're combining functions with functions. One of the killer parts of compose is if I have a really long compose, let me make sure this works before I keep talking, if I'm like, Oh, I didn't want to take two. I'll get the head of that. And I'll run that, and there we go. Well, the point of this is I've got three functions, and I can grab any sub, like any group, and pull that out into its own. So, I'll make this inefficientHead. I don't know how JS Bin works. Let me just define this first. So, what I'm doing here is just "extracting method" into its own composition. And now it still works. But the point is that when you glue functions together like this, you can kind of just grab the different parts. There's no data; there're no roots deep into your application. You're not depending on your environment and implicit inputs. Everything Joe went over in the first part is leading up to where---we should be comfortable with this right here because we're going to go further, and it's going to get insane. Oh, it looks better over here. No, it doesn't. It looks the same. You know what? There we go. But the whole point is that you make your grammar and then you use it as your application. So, what does my app do? Well, it gets the firstUpperOfDashes and then it does this inefficientHead on it. And that's exactly what it's doing. It's getting the first uppercase and then it's doing some inefficientHead head on it. And it's very readable and very abstract and high level and declarative. Any of this can change under the hood. And it's okay to use a function if you need to. If this was really hard to write, just write a function here and do it this way. No big deal. So, let me just use s.substring, and there we are. Can't read toUpperCase. I think I broke that. You've got to return. Oh, thank you. That's why I hired you. So, that was just to kind of sum that up, maybe answer someone's question over lunch about Why is this good? Well, use it and be like, Oh my gosh, this is super declarative, and it's reading like---all I'm doing is just composing bits and making this machine that's waiting for--- it's like those vending machines where you put in a quarter and get out a result. That's what's happening, what-the-hell is our quarter and W is our result. And it just goes through stuff. Now, there's different---this is kind of beautiful. We wrote apps like this for a year before we had any idea what a monad was. And it's JavaScript. It's loose. You don't really need to worry about that so much, but there're so many problems with doing it this way if you don't know how to compose bigger things. And that's what we're going to talk about in part 2. So, let's dive into it.
Category Theory
The Voyage. So, I love how big it sounds.-- (laughing) So, the point is that if I add 1 to 1, I get another number that's 2. And everybody should know exactly what that does even though you don't know what the implementation is because it's bigger than what I came up with. Somebody else came up with it a long time ago. And it's got some properties and laws with it. So, we know it's associative. That means it doesn't matter how it's grouped. It always just going to end up with the same result. It's commutative. We've got identity. And down at the bottom, we even know how it works with other functions. This is incredible. Can you imagine if all your functions had this book of, Oh, you can do this with it. And you can always guarantee we've got these theorems and formulas, you're basically---you don't have to know it. You can use add without that. But if you want to, boom! An ocean of knowledge. So, that's pretty beneficial. And with other---another cool thing about add, I'm basically regurgitating my fluent talk right now. But I'll get into other stuff. This right here is, you've got an intuition behind it, and it's polymorphic. So, we can add strings together, and we can add floats together. Floats are still numbers but in other languages, still works, big nums, whatever. Down at the bottom, we're adding arrays together. There's an intuition behind add, and those laws will still hold, well, maybe not for this particular crazy add, but when you work with these other functions that have these laws with them, they'll always hold for every type, which is really cool. So, you have an intuition and laws attached. Hey, guess what compose is? It's like add. It's got laws, and it's got properties, and it's actually polymorphic. You can use it on other things besides just functions. It's incredible. It's just like add. So, we're going to go into category theory. You're probably weeping at this point, like, What did I sign up for? But if nobody understands this type signature because I just threw it up on the screen, the (b->c) is a function that takes any variable---any type b to any type c. The second one is any function that takes a to b. And we'll return a function a to c. So, basically, it runs the middle one, then the first one, and ends up with the last one. But you guys know what compose does, and we'll see a lot more type signatures. It's not really important. Is that Haskell? That's a Haskell. It's a Henry Milner. There's no big deal with it. This is kind of a complicated one. I'll walk through them as we see them. You don't really need to know this one. But the important part about category theory is that the---you need a composition and an identity to form a category, and we're working with the function category when we're dealing with just normal compose and identity on functions. Does everybody know what the identity function is? Basically, you give it a value, and it just spits it right back out at you. So, you're like, Why would I need that? It's pretty valuable when you're doing point-free programming. You could do filter Id to refer to the thing going in. It's kind of weird. But the point is that you need an identity function and a composition function, and you can have a category. It's kind of a design pattern we're going to use in our programs. So, here there're laws associated with it. That means if I compose the left and right identity, it's always just going to be just like running the normal function. Because if identity just spits the argument right back at you, what's the point. It's always just going to work. Down at the bottom is associative. That's why we've been able to compose x amount of arguments. It doesn't matter how they're grouped. It's also why we're able to just grab any sub-composition, any two functions in a row in our compose, and pull it out into its own function. So, this isn't Haskell syntax now. My mouse is all crazy. That's weird. Oh, it's like a delay. So, like we saw over in this--- over here, we could grab out any two---like if I had f and g and h right here, I could just be like, Oh, these go together. Boom! And I'll make a new one called j, and it's the sub-composition of these. And the whole point---the reason for that is that compose is associative, so it doesn't matter how these are grouped together. So, you can just pull them out and they're their own functions. So, that's really neat. It's just like add. It doesn't matter if you add 1 and 2 or 2 and 3, they'll always end up in the same result.
Objects
I just wanted to tell you guys we're using a pattern from category theory, and category theory can tell you a lot about your programs if you're composing them like this. You don't need to know so much of that. I'm not going to give you a test on category theory later. I just want you guys to know that that's an important concept, and if you learn it, it's like learning more about add, or like, I didn't know multiplication distributed over it. I didn't need to know that to write my app. But it was cool to have it there. So, let's talk about this. So, we basically build up apps like Legos with functions combining with functions and data combining with functions and composing everything. Building new things from simpler, smaller things. But what about null checks? What about callbacks? What about error handling and side effects? The list goes on. I could probably list about 50 things of things we have to deal with every single day. So, we're going to reintroduce objects but not in the way you think of them. We're going to think of them as containers or wrappers for values. We're going to take a number or a string or a whatever and put it inside an object, and it's just going to wrap that value. So, we'll see that. We're not going to have methods. That sounds stupid. But, okay, we are going to have methods. I'm lying. But think of it as not having methods. And we're not going to think of them as nouns. We're not going to have a user object. We're not going to have a blog object. We're going to have different kinds of things that capture a behavior. And then, #4, you won't be making your own very often. I'm going to say that now because you probably won't for a while. And once you get into the heavier architecture design stuff, you can start doing this. But you can get very, very far for very long without having to find your own functor or something like that. So, don't worry about the implementation details or How do I make my own functor? Just be like here are some standard ones, and they'll probably capture everything I need anyway. So, Container. This is a convention I'm going to use throughout. And we've got this _Container that's an actual object. Then I've got a helper function that creates it by calling new. And the whole point of that is so we don't forget new and, too, because Container is a composable function now, it's just a constructor. So, I don't have to drop new in the middle of a compose somehow. I can just call Container as a function. So, dang that Jamaican food was good, you guys. Anyway, down at the bottom, what I want you to see is that Container(3), that is literally what it is. You are looking at a wrapper around the number 3. There is no function call and it returns something crazy, or it's not going as a property of an object and then it's going to have different names for it and stuff. We're really just putting Container around 3, and it's literally acting like a wrapper or container of it. And it takes a little bit to kind of get used to looking at that literally and not in an object-oriented sense. But down at the bottom is what you'd see kind of in the console. We have---we're assigning 3 to an arbitrary property called val, and we get our actual Container back. But, the Container(3) part is important. Don't worry about the 3. I just picked a random-- Does anybody have any questions about this part, online or-- Our video froze. It's because I blew the minds of too many people with my Container. So is everybody good? Does anybody have---I saw a lot of _____ of people who didn't have object-oriented experience in JavaScript, and so it might look at little foreign to you that the top thing is a constructor function if you call it with new. But it really is just a constructor function and then our object comes out. So, we're going to go into---alright, so what if I capitalize flamethrower? And I get a capitalized Flamethrower. That's great. I've got a function called capitalize. I can use that all day long. I've got all these little functions I've made based on other little functions. Is it still frozen? So, based on other little functions, and I want to use those functions everywhere. They're like my main jam. And the point is that---why is this red? Oh, what did I do. Oh, I hit it. When I said my main jam, I was like (guitar strum sound). But what if I try to capitalize a Container of a flamethrower. It's not going to work, right? Capitalize works on a string, not a Container. Functions don't take objects. They take standard values usually, and maybe this one takes an object, but, obviously, it doesn't because it takes a string right above it. So, we want to capitalize our flamethrower but we can't when we put it in a Container. That's a bummer. Now, you might be asking yourself Why I would put it in a Container? That sounds crazy. But we'll get to that. Just hang with me for a few more slides, and we'll talk about why we're putting flamethrower in a Container. Why wouldn't you put your flamethrower in a Container?
Object Map
There's this idea of map that if you map over the Container, you'll basically grab its value and pass it to the function. See, we've got our---let me, how do you, yeah. This s here is our flamethrower. See, it pulls out the flamethrower and gives it to the function, and now we can capitalize because this s is just a string. So, what we've done is open up our Container and given its value to the function. Then we get a closed---it closes it back up. So, let's look at that for a minute and think it over. Does anybody have any questions or are you kind of confused. Think about that. You look really upset with that. I guess the problem is that it's mixing concepts because there's object oriented that we're trying to---that we have to use because that's what JavaScript is underneath, right? Sure. And then somehow we're making a functional-- Just hang on to that. Scala does the same thing. We'll get to it _____, but the idea is that don't think of it as an object. Think of it as a Container for our string, and when we map over our Container, the string goes into the function we're mapping over. We're just opening up our Container, giving its value to the function. And so within that function, I've got my string. It's there, and I can capitalize it. And then when we're done mapping, it goes back into the Container. We're going to look at another example that'll probably clear this up. Oh, yeah, I should mention that you don't need to write a full function. I just wanted to do that to show you that it'll just pass that right into capitalize. Hey, Brian, there was a question if this is preferable to using valueOf in your Container object. Oh, valueOf is a hack on JavaScript that you can use to do some crazy stuff, but what I'm doing is I'm actually assigning a property to my object, and it's going to make the implementations easier. But if you guys want to experiment---I should mention that nobody's doing this right now. Get out there and play with it and get crazy, because maybe valueOf will be the solution that we've all been looking for. So, we're just using val as a convention. But look at the implementation of map real quick. See, I have to grab this .val, and that actually is--- if you look at map and you look at what's happening, we're literally running the function on the value inside the Container. That's what map is doing. It's running the function that's given to it on the value inside the Container. It's saying what it's doing. We're going to get more into the intuition and what we're doing here. But play around with valueOf. That's my answer. So, is the reason that we're doing this Container so that we can add functions into the prototype? The map for example. We will---that's exactly right. That's how we're going to get dynamic dispatch. So, this concept isn't foreign, right? Everybody knows how to map over an array. We were all just doing that. We have our Container(3). We map over it and add(1). I shouldn't say Container. We have our array of 4. We're mapping add(1) over it. We've got our Container(3). We're mapping add(1) over it. Now, most people think of map and they're, like, Iteration over a collection, for loop, I got what map does. That's just a specific implementation of map on an array. We're going to get into the general idea of map that works on anything. And that Container is just like a singleton array. It's got a value in it, and we're going to map a function over it. Are you guys good with me? Who's with me? Stick with it. I'll show you why we're doing this, but right now you're still probably just like, That's stupid. So, the true value of---the true idea is you go inside an object, and you run a function from within the object. So, we saw that function ran inside the Container. This is Joe's slide. It's so cool. (laughs) We go into our Container. One thing that's important to note about this is we've got---there's a lot going on on the slide I want to talk about. The top line here, we have our Container of 1, 2, 3. And we're going to map reverse over it and then map first over that. So, what we're going to do is basically when this goes into reverse, we get a reversed list. And then when we map over that reversed list and grab first, we get the value of 3. It's changing types inside the Container. It cannot have a type that is, like, This Container takes strings. It doesn't work. This Container has to take any value if it's going to be mapped over. Notice how that's composition. Isn't that weird? That top line, left to right composition. That's what we all wanted, right? And there's actually laws that enforce that that is composition. There's some weird theory behind this stuff that we're going to get into. But that's strange that we can map(map) and that's composition. Underneath that, we're going to get the length of the flamethrower and then add 1 to the length so we get 13. So, it changed from a string to a number, and then we added 1 to it. The top ones changed from an array to another array, and we grabbed the first one. So, you can chain maps. And the reason you can chain maps is because it keeps putting it back in the Container after you map. If I map reverse over 1, 2, 3, I will get a Container of 3, 2, 1. And if I map first over the Container of 3, 2, 1, I will get a Container of 3. It just keeps putting it back in the Container, so you can keep mapping. It's kind of like when people return this so you can keep chaining. It's that but in a more function sense because we're making copies. We're not just returning this. That's something else I should mention. We're making copies of Container along the way. You could probably optimize by not doing that, but we want to be immutable. So, we're going to spend a lot of time on functors because you guys have to understand them or else you're not going to understand monads or applicatives. Sorry, I'm getting distracted by the chat. Maybe I should just turn this down. So, what was this slide about. I just wanted to point out--so, like we saw before, we've got this map that takes its function and its argument. And we've got the map that we called dot on. That's why we're not using _.map. The bottom one--all it's doing is turning around and calling .map for us. So, the main difference here---these do exactly the same thing, these two maps. But the main difference is this one needs to exist. This Container(3) needs to exist so it can call .map on it. This Container(3) doesn't need to. I could just partially apply this map with add(1). Just like we've been partially applying things and currying things, the same difference, whatever. Scott would get mad at me for that. But the idea is that all we're going to do is define a map that we can just call dot on under the hood. Does everybody see that? It's second argument, we're just calling .map on it. This is going to be a pattern too just throughout. If you ever want to make something more functional and point free and composable, just take its object last and call dot under the hood. Did you say that the Container didn't need to exist in that second one? In that second one, I partially applied map because it's curried. So, I don't need a Container(3), I can just do map(add(1)). And the top one, what do I call dot on? If I don't have a Container, I can't call dot on anything. But when you call the curry function, you need something that has a map function in the prototype. Oh, I'm sorry. What I meant to say is that map since it's curried, I could just give it map(add), and I could partially apply it without that second argument, but the top one needs its-- okay. And in that way, we can build full machines of just composing functions and stuff without getting our data first. So, here're some examples of our new map. I just took the dot, and now we're just using the normal map we've always been using in this class, but we're not using the underscore dot. And we're going to call, like, Alright, what if our Container matches cat? We can't just call match on catsup. We have to map that over it because it's inside the Container. Match does not work with the Container. It works with the string inside the Container. So, we map over it. Just like if catsup was in a list, or in an array, and we wanted to map that match function over the array. You can't just give an array to match. You have to give the strings inside the array. So, you're basically mapping this function over the value inside the Container. So, let me know if you---how you guys are feeling about this because you're going to have to do exercises in a minute, and I'm going to be, like, I'm not helping. Compose underneath is just going to reverse dog and grab the first, so we'll get the g. That's my favorite example of reverse and first to get it last. So, that's just a way to use map. And we could've just called the dot syntax, but this is more composable and point free.
Maybe Functor
So, a functor. You guys have just learned about functors. Any object or data structure that you can map over, that implements a map function. And there are laws. So, it also has to obey these laws. But think of it as just if it has a map function, it is a functor unless you're jQuery and you don't do it right. So, we have---down at the bottom, getNameParts, cool. I wanted to wait for you, man. I didn't want you to miss this. I just defined a functor. It's anything with a map method. So, we've got a problem now. Let's put this into some real use, not just put stuff into Containers for no reason. Because Container didn't do anything for us. It just made things more complicated. So, here we can actually get an element with a query selector, so I passed the full_name Id selector into document.querySelector, which will get the element. And then we'll get the value out of that. We'll call .value on it basically. And then we're going to split it on I guess space. So, whatever you type into first full_name will come back with like an array of your first, last, and middle or something like that. So, we're going to get our name parts from this field. Well, what if it doesn't find full_name? Brian, we've got a couple of questions. Daniel B. is wondering if you can do a quick definition again on partial application since he's not sure what that means. It's just giving a curried function less arguments than it takes. So, you're partially applying it because you're only giving it part of it. There is some technical details that you might want to say currying is this and partial application is that. I use them interchangeably. It doesn't really matter to me. So, you can just give---split, for instance, right there, is being partially applied with the space instead of the whole thing, instead of the space and the string, which would run it. So, what if it doesn't find the---is there other questions? Do we curry so we don't have to check for existence, or is the lack of existence check a result of currying? They don't relate. You curry because you want to concisely define in a generic way a function from a--- by giving it some data. If I wanted to---right there on get('value'), I've made a new function by only giving it it's first argument. But we're going to get into nulls. This is just the slide one of them pesky nulls. There's another one. If querySelector doesn't find full_name, if we misspell it or we forget that underscore, it's going to be null on getElement. And then when I call get('value'), it's going to be, like, I can't get value of null, I'm just going to die. And if you try to split that, it's just going to go down the tubes, down the drain. Boom! is what I mean. So, it blows up because there needs to be a null check. Now, we could implement some crazy function that wraps everything and say, like, Oh, right, if it gets the value, combinator, check null first, and then run these functions, and that's possible. It's functional. Do it. But there's a cooler way that gives you some theory and stuff behind it. Let's meet our first functor. I made these baseball looking cars for each one. They're from 1991 or 1989. Looks like Saved By The Bell. Anyway, so this is going to capture a null check. It's the Maybe functor. The value may or may not be there. And you can see a bunch of different implementations. In Scala, for instance, it's Option and you have Some and None. In Haskell, you're going to have Just and Nothing. We're going to simplify this a bit by rolling these subclasses into just the one type because it's easier to just grock it first. But later on, it'll work. You'll get it. It works like Either when we look at that. So, the idea here is that you may or may not have a value in your Container. Let's look at that. And, again, this is a little bit of a simplified version. But the---all I'm doing is I took out the if not null and if not undefined, but right now it's just checking if it's truthy. But the idea is that if it's there, we're going to apply the function. And if it's not, we're just going to return null, whatever, don't do it. So, now if we map capitalize over flamethrower, we get it just like Container. But we have a null check in our map. It's going to do our null check every time we map over it. So, if we call capitalize on Maybe(null), it doesn't run it. So, what you're saying is the _Maybe is now your Container. Yes. You've redefined the Container into that? Well, in addition to Container, we've defined Maybe. And, by the way, I have this _Maybe, and then down there I'm just calling Maybe without underscore, that's my constructor helper that calls new. But this is pretty cool. Check this out. If you look at how we're calling this function on the value inside the Maybe, this .val is the property inside Maybe or in its prototype, we can look at its internal value and decide whether or not to run this. We've abstracted function application. That is so powerful. We can say, like, Oh, you want to run this function on me? No. So, this is pretty great. Every step of the way, if you put your value in a Maybe, it will do a null check, and it won't run if it's null. Now, let's look at another example I suppose. This is one just using compose. So, if we have this function that's just going to fine the first match, and we do dogsup, there's no match of cat and dogsup, so it blows up because we can't call first on null. Are people still being tripped up? I know this is pretty new for you guys. You just saw compose. You just saw partial application. We're using both. We're partially applying match, which is waiting for a string to tell us-- So, it's going to be a lot of new information. But I want you guys to get the stuff, be able to take it home, play with the---do the exercises and get a real feel for it. But right now, ask me if you have questions, we'll get through it. So, if it blows up, right in between that match and first, we stuck a Maybe and then we mapped first over it. Let's go back and forth a few times. Alright, camera one. Camera two. Camera one. Camera two. I'll hold it on here for a minute then go back so everybody sees. Wouldn't you want to somehow wrap what you're passing in? Well, dogsup is there. But the match may return a null. It might not match cat, which returns null in JavaScript because match is---it'll either return the match or a null. Or, actually, it returns an array of---anyway, or a null, which is stupid, because it should return an empty array and it would've worked like Maybe. But, anyway, the point is that once---at the point of when it becomes null, at this point right here, I keep forgetting about this wonderful green thing, at this point right here, we might have a null, so we're going to drop it in the maybe by running the Maybe constructor. It's just if we look a little bit back, Maybe's just a function, right? If I---down at the bottom where I put the flamethrower and Maybe, I'm just calling maybe with flamethrower. So, I could just drop Maybe in the middle of a composition and whatever's there goes into Maybe. So, here if Match might return a null in between Match and first, and there's going to be a null right here and it's blowing up my app, I could drop Maybe there and put it in there, and then I can map first over it because I can't call first on Maybe. I have to map first over Maybe. If I don't map first over Maybe, first will be, like, I can't---it'll be like bracket or something because it's going to get the string of the object and try to be object-object. So, the point is that you have null. We're going to drop in a Maybe and map over that. We're going to see this more and more. And we're about to do some exercises so you guys can play with it yourselves. But we need to lock this down because this is the easiest functor.
Functors Exercise 1
I lied. Container is actually called Identity. So, we're going to look at Identity here and just pretend it's Container. Same thing, different name. So, let's do exercise 1 together. It'll run a little bit differently, but same idea. We've got our test down here that's going to run our example and pass or not. So, we have to define how to---we're going to use add and map to make a function that increments in value inside a functor. Does anybody want to give me some help? Does anybody kind of get this stuff? Let's try it. I strongly encourage you to just open this up and start playing with it, start wailing on it. Over here, I could say, What if I had Identity(3)? Oh, this is this big gross thing. Let me do toString on it so it looks nice. So, Identity(3) literally. How do I add one to that? I can't do add(1) with Identity(3), right? That's not going to work. I get 1Identity(3). That doesn't work. I wanted to add 1 inside the Identity. We were learning about this map function a minute ago, so that's probably what it's going to be. Does anybody want to give me a little help at this point? Okay, we'll keep going. I want us to understand this, so don't let me go on until we get this. Somebody's asking what point free in JS Bin is. Point free is a library that implements the fantasyland spec that we'll talk about later but in a point-free way. Valentine's got it. So, we want to map add(1) over Identity of whatever. I'm just writing it here so we could see. So, that'll be---see, it looks a little crazy like that. Let me do it toString here. JS Bin outputs the whole thing for some reason. If you did it in the console, it would look a lot nice. But, we're going to get Identity(4). This isn't the solution. I want a function that increments a value inside any functor. There it is. I'm just going to map(_add(1) over whatever functor comes in. So, let's try this out and see if it works. Okay, exercise 1 is okay. So, let's talk about that. Let's beat it up. Ask me questions. Tell me why I'm wrong. Let's talk about it. Anybody. You there in the hoodie. I thought map took a lot of arguments. No, it's curried, so we're only giving it one right here, just like add takes more than one argument, and that's curried. So, we're mapping curried with add--- normally we'd just do it like second argument Identity(2), etc. So, that's a great question. We just went over that in part 1. But part of map, part of what everybody is hardwired to think map does is run many times. Everybody's like, Map will run many times. And it doesn't. If you have an array of one item, it runs once. If you have a Container with one thing in it, it runs once. If you have a Maybe with one thing in it, it's going to maybe run that once. So, think of it as---map is not about iterating. It's about going inside of an object or a data structure and running a function from within that data structure on its properties or behavior. Identity will become a little bit clearer later. The main thing to think about right now is just like a neutral functor that you can play with and see---get a feel for map with no added behavior. So, let's do another one. Let's see---so I'm going to map this real function. This is a little bit clearer. So, I'm going to get x. And x will be whatever's inside my Identity. And I'm going to do x.toUpperCase. Awesome, this is going to be the best. And then I have my Identity of let's do someString, and that doesn't work. Return maybe. Let's do it over here on the left. I'm just going to do it over here and name it. So, it takes x. In this case, let's make x firehose to commiserate. And then we'll return x.toUppercase. Now, let's run this. And you know what I'm going to do? I'm going to call toString on thing at the end. Thing is already there, thing.toString. This---seriously, having to call it toString is not common. It's just because we're in JS Bin. So, let's try a different one. So, x became---let's give ourselves some room here---x became FIREHOSE inside the map. And it worked all inside Identity. So, mapping this function over Identity of firehose, x became the FIREHOSE. We uppercased it. And then what we get is Identity with the uppercased FIREHOSE in it. Neat. So, we ran a function inside of Identity. Let's think of another example. Let's do---we'll do an array, why not? And we'll just do reverse on that. The destructive impure reverse. So, we'll do our thing.toString. There it is, it reversed it. Are you guys getting a little bit of a feel for it? Are you playing with it? Are you just staring at it and trying to understand why the hell map is working on the Identity? We're having some confusion in the chat on what thing is, what we're going over with that. Oh, I'm sorry, it is our test function we're defining to play with because this is kind of hard to work in, so I'm going to work over here and then just run it over here. It's actually not our test function, to be honest, it's our---there it is. Now it's a function. So, if I call test function with Identity, toString. So, let me think of one more example here. We've got a---I've got to let you guys play with this. I've got to cut you free. You have to fly. Somebody asks, Does Identity act as a list within a map call? No, it doesn't. There is no array going on here. This is a totally different map than you're used to. So, Identity is just sitting around something, anything. Let's make it---does anybody know the date.getDay or something like that? So, now we're going to map this over a new Date. Let's see if that works. That's weird, what did I do? Did you run it first? But it should already have run the map over the Identity of the new Date. Did I do it wrong? I see, you've got to run it to refresh it. ToString of undefined. I don't know what the actual date.getDay function is. It doesn't matter. The point is that Identity has anything inside it. It can be a date, x becomes the date. It could be a string, x becomes the string. It could be whatever. Identity is like a list of one thing. So, if I have two, I could make 2 + 2, right? X becomes 2. Does everybody see that? Hey, Brian, there's a question from James M. in chat on whether this I think he means Identity is kind of like a with block in JavaScript or Python. Don't think of it that way. You're just going to destroy your brain. I mean if that's the way that makes sense to you sort of, but I guess you can see that you're getting the context---it's not going to change this if that's what you're worried about. We're not going to use this at all, which is pretty great, except for inside the implementations of objects, which you don't have to deal with. Yay, no this! So, let's run this and try to do this with the list here just to make sure. I get a 4 out because I called toString on it. I have a question Brian. Is Identity---are you showing Identity just because it's the easiest functor or is it something you actually use much compared to Maybe? Identity is cool because what it does is allow you to lift things into the functor world. Even if you don't want any side effects with it, it can compose with other functors now. It's kind of the basis of monad transformers, and you end up using it just like Id. When you see the Id function, you're like, What would I ever use the Id function for? And then you actually start doing point-free code, and you're like, Oh, I need that. It's just like this neutral functor if you need a functor and you don't want any side effects to satisfy some type signature. Or, let's say some jerk made their function with map around it without making it--- calling map later.
Functors Exercise 2
So, everybody play around with Identity. See if you can get exercise 1 to pass and exercise 2, and ask me questions. Hey, Brian, could you look at Mims W. on chat to double check her understanding---his or her understanding so far. Yes, totally right. Mims W., to clear this up, map is not list map. Map is my map that just calls map on whatever it gets. Let's clear this up a little bit just to make sure you guys understand. I have an Identity(2) and I can map(add(1)) to it, and then I should get an Identity(3). So, let's run it and look at our result.toString. Sorry, that always gets me. There it is. So, this map is the same as this map up here where I can do Identity(2) or whatever, a string(2) or 29 or an array of the string of 29. So, the idea is that map is not list map. It's a different map that works on anything. So, the slide where you brought in PointFree, that's what has that implementation in it? Is that what I'm seeing up on lines 3 and 4? Map is the PointFree's map. What it's going to do is exactly--- Mims pointed this out---am I saying that right? Map is the one I showed right here and then glazed over and walked away from. There it is. That's my map. It just calls map on whatever object it is. But it's curried, so it works like a list map, but it works on any functor. A list is a functor, or an array is a functor. And so is Identity and so is Maybe. So, what we are really going to be doing is calling .map when you call this line. It's like one thing that's really cool is this map is like, I'm waiting for my second argument. It could be any functor at all, I'm just going to call .map on it. It's an interface. It's .mappable. Do you know what I mean? So, up here you're---you need your specific thing, but down there you don't. Christian T. wants to make a note that at some point he would love to hear if there're any production-ready libraries to do this fantasyland-type file. We are---we will be showing you the libraries that we're going to use in our demo later. But we'll go through a couple of the libraries we're using. I have to say the state of libraries is not where it should be because a lot of this is really new. It's like if you picked up Node the day it was invented and you were like, Oh, man, where's all my plugins? I'm like, Go make them. You're going to be awesome for making them. Strategy design pattern. Cool analogy. Passing in a function as a strategy. Beautiful. If you get rid of the word functor from your vernacular and you just say mappable. I want to map over a mappable. To cut to the chase, the production-ready code is few and far between. But what's good about this stuff is it's out there, and it's scattered, and it needs help. Just so you guys understand example 2, xs is an Identity of a list. It's not a list. It has---I keep saying list. It has an array inside the Identity. So, you can't call head on Identity. You have to map head over Identity, so head will work on the list, not the Identity itself. You can think of it as--- if I want to add(1) to a list. Let me do that. So, if this add(1) function, let me just call it inc. If I want to inc a list, that's not going to work. That's just going to blow up. And it's not because there's a lot of things. It's because it's inside a list. And inc expects this number. Even if there's one thing in the list, it's not going to work. You have to map inc over that list to get a new list of 5. And in that exact same way, if it wasn't in the list, it was instead in Identity, we have to map inc over Identity, because inc doesn't take Identity. It takes the value inside of it. So, now we'll get an Identity(5). And if you want to get real nuts, we have an Identity of a list, and we want to inc over that, we're going to map(map), for those of you who aren't quite crying yet. Raise your hand if you do understand it. We've got three. Let's talk about 2. Do you guys want to do 2 together? These are supposed to take time. And you're supposed to play with it and learn from it. I'm not like, Hurry! So, if you don't want to go over it, tell me to hold on. I don't want to spoil it for you. How about you guys online? Christian T. wants me to go over it? Or is he like, I know it. Franco's good. Looks like Miles is either drowning or happy. There're some stuck on 2 and some good on 2. There're some stuck on 3.
Functors Exercise 2 Solution
Let's go over 2 and then let people do 3. Basically, we just kind of did it, but let's look at this. Let me make sure if I run it---Ramda is not defined. Undefined is not a function because I have not defined the function. So, let's do that. So, what my test says, Hey, I'm actually expecting Identity of do, which is the first thing in this list. And I'm going to run it with xs, which is an Identity of this list. So, you don't have to think too much about it. We want to say, use head to get the first element of the list. But we're going to map over Identity to do that. So, we're going to map head over the Identity. Oh, well, Identity gets passed in. Is that what's tripping you guys up? The reason I wanted to do this exercise---they're all making functions, they're not actually running functions. And I'm running the function in the test, which I probably should have thought about a little bit more. What's cool about this is it doesn't have to take Identity. It could take Maybe. It could take a list, any functor. So, of course, the functor has to have a list inside it or else head will blow up. But mapping over any functor---map is just going to dynamically dispatch on any functor it gets. So, if I give it an Identity of this, it'll work. So, let's run that. I could totally understand why that tripped a lot of people up. What'd I do wrong? Did I not do 1? We're going to do 1 together. Why not? Sorry, guys, did I miss that? No, you did 1 already, and then you blew it away. So, we've got 1 and 2 good. So, we're just going to map(_.add(1)). We're just going to map(_.head). It's really not that difficult. It's just like working with lists, but it's a different type, and you're like, Why does that work? I don't like it. We've got #3, like it. So, for a lot of people online, I don't know if there're a lot of people in the chatroom or what, but if you're out there and you're confused, I'm here now, and you paid, so ask me questions, or Joe. I'll wait another minute or two before we do 3 and then---how many exercises are there? I think one of them has 6. We don't have to do them all. Is it the---so, we're using currying and partial application of map here with head. So, I could write this another way to say, and that'll get me--- that'll get me an Identity('blah'). But up here we just partially applied it without calling the whole thing. I could just use xs, which is an Identity of this list. So, this'll actually run it. This'll return me a function that takes an Identity or some functor. I'll take this opportunity to talk about how much I love the Gin Blossoms. I was just telling Joe it came on in the grocery store the other day, and I was like, Yeah! And now I hate what I've become. James, head is in Ramda. We've been using Ramda this whole time. There's maybe one other function we might see from it that hasn't been in the other examples. Sorry for not going over that. It's just like first. You've got the Ramda creators in the chatroom. I have a 20-second delay, right? So, when you see their chat, they're going to hear your answer. Someone's asking what is-- Well, let me talk about---for the people that are bored, and while other people are working, I'll wax theoretical here. Map lifted head into a functor context. You've raised this function that just worked on something normal into something that worked on a functor. So, we've transformed this function into a new function. That's kind of cool. That's just like list. If I have a function that just gets, I don't know, I'm running out of cheap, easy examples, but let's say, let's just replace as for bs and var ab. That's an absolute value, isn't it? A4b. It thinks it's s. I don't know, Ramda get on it. Why isn't this in there? It should be there. So, a4b, this just works on a string, right? If I want it to work on an array, I could lift it into an array function by just, boom! Now, a4bs works on an array, not just one value. We've broken the problem down into each element, and we're just going to run it on the one thing, and then we'll just map over it if we need to. Now, we've got a double purpose. I can just call this inline. And a lot of people don't do this right now because they have the ceremony of the glue code and the names and the return and the blah blah blah. But the point is that map---if you just surround a function now, you just partially apply it, boom!, now this function works on many things. It works on Maybes, Identities, anything. It works on functors. Depending on the functor, this will run many times. This might not run. This will run with no effects at all. So, when you use the term 'lift,' you're just saying take a standard function and make it work with any functor. That's exactly right. Let's look at the implementation of map on Container. Look at that---where did f go? It went inside of the Container. We've lifted it into the Container and ran the function from within the Container. Same thing with Maybe. Let's look at Maybe. But this one is more interesting, so I wanted to look at this one instead. We've lifted capitalize into the Maybe context to run it there. It doesn't run here. It doesn't run capitalize on flamethrower. It runs capitalize on flamethrower inside the Maybe. We've lifted it in there. So, the power of running a function from within a context allows you to abstract function application---you can do all sorts of crazy stuff. You can do type conversions, and you can do run many times like a list. You can do Maybe, Maybe not. Now, you have to obey these laws, which we will look at, and that kind of restricts what you can do here. But, when you get the idea of map, you get a pretty good intuition of, Oh, it's map. I'm just going to go on there and map it. So, if you're working in Scala and you get an option, you're like, I want the thing inside the option. You can't get the thing inside the option. You have to map over it.
Functors Exercise 3 Solution
Let's do exercise 3 together, and 4 is kind of a bonus. I should have put a star--I put a little star next to the bonus ones. But it's not that hard. I'll let you guys work on 4, but it's definitely harder because you have to write the Maybe yourself, like call it yourself. So, use safeGet and head again here to find the first initial of the user. Did anybody have any snags on this before I go into it? You've got a snag on it? What did you try. I'll write what you tried, and then we'll talk about it. I wrote map, and then head, and then safeGet. Oh, so close! We're mapping the head function over the safeGet function here. You're so close. You just have to compose them. Because we want to first safeGet, then get the head. Is that the answer? Let's see. It pulls the map ahead. What did I do? SafeGet--I gave it a parameter of name. See, now that's what I have, but it doesn't work. I wrapped a compose around everything. I did a _____ composer on a map of the head. And did that work? Yes. That's right I'm backwards. So, let's talk about this. I have a syntax error up there. You should feel like a champion. This is a good one. Let's talk about this. Oh, by the way, must so you guys understand--I've got my exercise and description, some helper stuff and data, and then it's like, Start it, and this is where it starts. And then the test. It's like that format on every single one. So, when you see the ex3, that's usually what you have to work on. The rest is just kind of noise but helpful. So, yeah, let's talk about this. What does safeGet do? SafeGet gets a---it takes my---it works just like get, but it puts it inside of Maybe, right? Because you might not have a property. This user might not have a name. So, if I call safeGet('name') on user, it's going to return me a Maybe right here. Do you guys see right here we'll have a Maybe("Albert"). That's what's happening right after the safeGet. And we have to map head over that. We can't call head on the Maybe. We have to call it on the string inside the Maybe. So, we map over the Maybe("Albert"). So, the point is that the first function returns us a functor. Then we map head over that functor. One cool thing to note is if I just do null here, it's going to fail the test, but it's not going to blow up. Expected Maybe(A) to match Maybe(null). It never ran head. Now, that'll save you giant stack traces, whole things like let's say you don't have a current user, and you don't want to display their name if there's no current user. Drop them in a Maybe and map over it. I don't know if they're there or not. But if they're not, I just won't run this. So, you essentially put it in the box, the Maybe box, and say, I don't know what's in this box. It may or may not be there, and when you map over it, it may or may not run. But that's very safe as a programmer to force the rest of your code to deal with the fact that thing may or may not be there. Things can't just run on it anymore. It has to map over it. And you get a right-time safety that you don't get unless you do that. The rest of your code will now not blow up from a random null. Or if you wrote a library, and you returned a Maybe, that one time that it returned a null and just blindsided the app, they would have had to deal with it. They would have had to map over it in the first place when they wrote it rather than just getting hit with a null later. It is totally Schrodinger's cat. You can't map over the safeGet because it's not a Maybe yet. SafeGet takes a user, then returns a Maybe of its name. It's not a Maybe going into it. So, you don't have anything to map over. You're just getting a user. Do you guys see that? Right here when I compose, what's coming in is a---it starts out---it's like a user right here, and then after it gets through the safeGet, I have a Maybe of user right about here. And then I have to map over that. Now, let's get back to Albert. Let's do the last one and talk more about functors because we've got to know them.
Functors Exercise 4 Solution
This one's pretty great. So, here's what you're going to see in your normal code. This is like what you're going to see in your day to day. And we're going to say if we have a number, we want to parse the int. Okay, well I know how to write that. I could say, well, I have parseInt. And what do I want to do? I want to compose that with---first put it in a Maybe because it may or may not be there. But then I have to map parseInt over it. And we'll see if this works. It worked. Let's make it weird. Things got weird. Things got real weird. Expected Maybe(4) to equal Maybe(43). I just like to fail it sometimes just to know we're on the right track. It's a good little tip. You can just mess with the tests so you know that you did it right. We're getting asked for a more step-by-step breakdown of how you're coming up with the solution. So, with this example 4, I've said I've noticed---I've recognized a pattern that says if this value is here, I guess you'd more likely see this, if it's not == null && it's not == undefined, and we'll see this every day in JavaScript code. People write this over and over and over again. Then we want to do this. And we're saying, Okay, I know that there's a pattern that captures this. I know that's called Maybe. If I have a null check and I want to run something if it's there, I will use Maybe. Well, how do I use Maybe? Well, I know I want to put my value in it. That's an important part. Functions don't go into Maybes. Values go into Maybes. So, if I have a Maybe(2), then if I parseInt---I can't parseInt to Maybe(2). I have to map parseInt to Maybe(2), then I'll---oh, it's a string, then I'll go to Maybe(2). So, I was just kind of pushed into this solution because I said I have to put n in a maybe because it might not be there. My value goes in the Maybe, in that container, and then when I map this function over it, it checks it. I realize that the value's in the container now, so I have to map this function over it. Now, the composition of these two things, putting it in the maybe, then mapping parseInt over it, is kind of what we're looking for to gain a function instead of just running it immediately. But you're going to see this a lot in your code because most of your code is going to be making functions like that. You're not just going to be running things left and right inside other functions. You're going to be composing and combining things into new functions. So, that was 4. How are we doing? Was that a better breakdown? So, here's our log function. I think in other ones I've defined this for you just to help a little bit. So, console.log and return it. And there will be log in the middle. What is this thing? This is the JS Bin specific version of an output of an object that I've never seen in any other interpreter, but it says our value is 4, and it's inside this giant object. If I call toString on this, it'll look a lot nicer. So, is it maybe the return X? No not that.-- So, exercise 5 says, so we're going to log---yeah, it looks very close, like exactly what I had. Let's see. Excercise 5 log. Oh, it's calling it exercise 4. (laughs) Sorry man, that's a bummer man! That's why I shouldn't create exercise 5. Actually, when I was naming these, I was like, These are horrible names. Every time I'd rearrange it after every exercise.
Functors Exercise Wrap-up
Hey Brian. Yeah. We've got one question that asks is there any way to get the value out of a functor? That's a great, great question. Um. And a request to go over exercise 3 again because someone's still stuck on exercise 3. I'm going to talk about getting the value out. In short, no. Why if it wasn't there would it be there later? You're like, Oh, this might be there. And there later, you're like, It's cool, it's there. And then, boom! If you're going to put it in a Maybe, it's not just going to materialize later. If it's null here, it's going to be null there. Now, if you're---and the point is the reason for that is it's not like you're mapping functions over it. Those functions won't run. It just doesn't do anything. It just skips your whole app at that point. Every time you map over it, it just doesn't do anything. So, if you have a value that may be null, and you put it in a Maybe, you don't take it out because you're just putting in something and taking it back out for no reason. You've lost all the benefit. You keep it there to let everybody know it's there. And then eventually---I'm answering the question about do you get the value out of the Maybe or how do you get the value out of the Maybe. You will eventually do something with it, whether it's put it on a screen, log it out, fill in the URL bar, something, make an API call. Something will happen with the value inside the Maybe. So, when it gets there, you either run it or you don't. But you don't take it out in the middle of your composition. I was going to go over 3 again, right? Let's do a step-by-step one of this again. So, example 3. It asks us to do the---I'm glad we're doing this, by the way. This is the hardest part of the class, right here, right now. That's why I was so serious about us knowing this, asking questions, spending our time on it. There's going to be a ton more functors. There's nothing you need to know more than, Oh, I just map over it. Done, done, done. We have safeGet and head, and we're going to try to find the first initial of the user. So, what's happening? In our exercise 3, we're getting the user, which is this data right here, so we're going to use safeGet to get its name. So, I know I need to call safeGet with name to pull the name out of the user. If you hadn't been doing the other ones, we had this Get function, and this is just the safe version of that because it returns us a Maybe because it might not be there whenever you pluck the value or get the value or grab the property of the user. This thing might not be there. It might be null. Here it is, safeGet('name', {Id: 3}). There is no name there. Do I have to run it first? There it is. We get a value of undefined. Let me call toString on that so you guys can see it better. These toStrings shouldn't be normal. Just want you guys to know that. It's just because we're in JS Bin. But if we have a name, I don't know, Gman. Let's do my name. So, we gave Maybe a brian. So, we've got this user with the name brian, and we got the name, and it comes out as Maybe(brian), but if I gave the user with no name, I got a Maybe(null). So safeGet will put it in a Maybe because it knows it may or may not be there. That gives us a way to tell the rest of our program and the rest of the programmers and our future self this thing is going to be null later, and I've marked it. I've put it in this box that says now everything is impossible for you to understand. So, we have safeGet, we got the name, we got this Maybe(brian). Or Maybe(albert). Well, what do I want to do? I want to get---to call head to get the first initial of that user. So, it will grab the first thing. So, I want to call head on that. Well, that doesn't work because safeGet is a Maybe(brian). I can't call head on a Maybe(brian). That doesn't work. I don't even know what that does. But I can map over it. There it is. I get b. Again with these two strings. So, it mapped head over Maybe(brian). And the way that gives me safety is if I map head over Maybe(null), nothing happens. If I call toString, Maybe(null). It's fine. It doesn't break. So, instead of head, I have to map head over that, right? And something that's been tripping us up throughout, but I'm glad because you guys are going to have to hit this a lot if you're trying to write programs like this. This is probably not the way to do this. You want to partially apply map and compose these things together. And it'll be run later. Everything will be run later by the callers. You just want to build up your app of your functions glued together. You don't want to be like running things left and right while you're making your app. There you go. So, that's our solution. I want to address one thing before we go on. And that is that I was talking---what was your name? I was just talking to Phil, and we were talking about---so, first of all, almost always---my app is just a bunch of functions glued together, whatever, and I'm going to get one piece of data from outside. I'm not getting 16 different inputs coming into my function. I'm just going to get something. For instance, let's say I'm going to get an API call. Let's do Api.get('/users/3'). So, that's my input to my app. So, I did an API call to get a user. And now I have data in my app. My app's come alive with this data. I'm not getting---that's my entry point. And once I've got my data, let's say that user might not be there. I'll just drop him in a Maybe, or her in a Maybe. And then I'll map(theRestOfMyApp) over it. Maybe that's UsersController, whatever. But the point is once I map the rest of my app over that, it's either not going to run at all or it's going to run and that's it. So, you're not dealing with Maybe like, Oh, man, Maybe's all over the place. You really, once you map once, you really go to the rest of your app typically. Now, I could do this too if I wanted to. I could map other stuff. But, typically, this is what it's going to look like. You have the rest of your app to map over. If it's null, you don't want to run it. Now, one other thing that we were talking about was---let me put a function here. I'll do it the other syntax way because it's easier to write. And my user here. Now, I have a user and I can be like var u is blah. And I can do imperative code. But that's what I'm saying is now you have your user for the rest of your stuff, if it's not there, if it's there. I guess I have to put this in a Maybe. We'll get to this in a second. But the idea is that you can pass your---you don't have to be point free. You don't have to make it without arguments. You can---your Maybe value will be passed in there, and you can run the rest of your app in here too. So, I can make some call to UsersController.renderIndex with my User. Done. Or if I want to get fancy, I could bring it back out to being a first-class function, and now we're good. Back to the presentation. We have a couple of questions about that stuff that you just showed. First is What is API, because the video is frozen, and the second is---just answer that first and then--. Okay, API was just a fictional API library I was using to get some user from the API call. It's not real. And the second one, Vulcan was asking Doesn't the API being generally async break the rule of statelessness. Hold on my friend. You are this close. We're about to learn about functors that run in the future. So, hang tight.
Either
I'm going to go through Either kind of fast. What other talk was that, I don't know. I've got to dim the screen so I don't get distracted. So, Either is just like Maybe, but it's used for error handling, which is kind of neat. In a pure, functional way, you can handle errors. Now, it's like Maybe but we do have two subclasses this time. It's not going to be just a Maybe null or Maybe something else. And if you see other implementations of Maybe, you're going to see Just and Nothing, and it's going to look a lot more like Either here. So, let's look at how this works. Just like Maybe in Container, if I run over our Right, the x becomes the 2, and I add 1 to it. Beautiful. But Left, just like a Maybe(null), it does not run it at all. But what's cool about a Left, it has some error message in it. It's just kind of embedded in the type. It's not going to run the function, but it will return it with the message in there. So, I could use that for errors. I could say, If it's good, keep running it with the value inside of it. If it's not, just return a Left with the error message in it. And those two types, map works differently with both types. So, if I decide somewhere in my app to return a Left, boom! App stops running altogether. Error message just ends up propagating to the end. Now, we were just talking about that. You may or may not have the value in a Maybe. You might have a Left and you might have a Right, and you don't know. And you're telling your app that, and you're coding to that. You're writing a safe app aware of the fact it may or may not be there. Now, if you say, Yeah, I don't want that anymore. I want to go back to how it was. You can go back to being unsafe, and you can get the value out. You really can. There are some functions that will do that. There is lowercase either that takes two functions to run--one on if it's the left side and one on if it's the right side. It'll actually pick apart the type and get you back out. Same with Maybe. It'll take a function to run if it's null or otherwise. Because sometimes you want to take some action. You don't want to just ignore the rest of your app. You want to take some action if it's null or take some action if it's a Left. But for the most part, these abstractions are wonderful, and you shouldn't be using those functions. That's the wrong way to do it. If you have to use one of those functions, it's like 2% of the time, and you're probably doing it wrong, unless there's a specific action you want to take instead of just returning the error message. So, let's look at how this works a little bit more. This should be kind of easier to understand at this point. It's just like a Maybe, just like an Identity, just like a list. We're saying---but it's a little bit more complicated because there're two types involved--its Left and its Right. So, if I say, Hey, determine the age of this user, and then we'll add 1 to it because I don't know what else to do, I'm bored. I've run out of examples that are interesting. But the main thing is, it says if it does have an age, return it. And if it doesn't, tell us we couldn't get age. And, boom!, map just does the right thing. We're either going to add 1 to the age, or we're not going to get anything out, and it's just going to return that error message back. Do you guys see how that works as an error handling? Hey, Brian, there're a couple of questions in chat here. One on what's Right and Left and whether they're part of the point-free lib. All these libraries will be included in the examples and in the demo apps so you can look at them further. But these are two subclasses of the Either library. There's a---we're using Folktale just for those who are impatient for this one. And so it's Folktale's Either library with two subclasses. The other question is Right kind of like myFunc or Val while Left is myVal and myFunc? No, I, at some point, put an Either up. That was wrong, and it messed up a lot of people's brains. So, sorry about that. Either is specifically Left will never run. It has the error in it. Right will always run, you're good. But it is a disjunction operator. You've got Left or Right. It's just that when you map over it, you never run the Left. So, it's disjunction in that---you know what I'm saying, it's the or in that sense. Left and Right is---oh, I should tell you, it's Left and Right, and Either has two properties--it's Left and it's Right. It's like X and Y, I don't know. So, it's either Left side or Right side of the Either. If you think of it as an or operator, it's Left or Right. Moving on, I'm not going to spend too much time on Either because you guys should kind of just understand that, hey, it's going to run on a Right or it's not going to run on a Left. And just remember the part, that top line in determineAge, that if you want it to run, return a Right, if you want it to stop, return a Left. There's not much more to it. And the reason I'm not going to spend too much time on it is when you're doing asynchronous stuff, which we are all the time, always, it kind of isn't as useful as when you're doing synchronous stuff.
IO
IO--this one's pretty crazy. You guys ready for pure IO? This is a functor. But instead of a number or a string or a list inside it, it's going to put a function inside it. That's pretty crazy. So, it's like a computation builder. Every time you map over an IO, it's going to basically secretly compose inside. So, we'll see it in action. This is going to get crazy. But until now, we've seen values that are just like kind of standard values that are data in your app going through your app. IO is taking a function, and we'll look at that, and it's going to be a little bit different in that regard. But you can use it to contain side effects. And eventually it just returns you a function you call runIO on. So, it's just like a lazy computation. Your app doesn't do anything. It just builds up a computation for you, then you run it. Let's look at this. So, if I have an IO of a function that gets the email's value---I probably should have used document querySelector. Anyway, I'm using jQuery here. So, shout out to Resig Map Rules. So, the IO is taking a function that returns us whatever's in the email field's value. Now, if I want to say, Welcome, whatever your name is, I concat welcome to it. I have to map over it because it's inside the IO. So, what I'm doing is mapping a function over the function. It's kind of weird. You don't think of it that way though. You map this function over that eventual value. So, it'd be the value of the email field just like we've been seeing that goes into concat. So, we'll say, Welcome steve@foodie.NET. So, it builds up a really lazy computation. At the end you call runIO with it. Let's see another example. That's runIO. You've set up your---you've made your computation throughout your app, and instead of it running, it just built more things up. And then you finally call runIO with it. So, let's see here. If I want to get the preferences out of a store, let's look at this part right up here. If I get the preferences at a store---let's pretend this thing returns me an IO. If I'm getting it out of the local data storage or whatever, it's a secret input. It's input into my app that I have to be honest about. I can't just be like, Magic, there's data somewhere. It just showed up. It should be known that this thing's coming from the outside world into my app. And I want to flag that by the Store.get("preferences") will return me an IO of preferences. It's not just going to return me preferences right off the bat. So, nothing will happen when I run my app. Which is really nice for testability and extendibility. I can keep building upon my app without it ever running. So, if I may getBgColor, we'll parse those preferences, because they're presumably a JSON string, and we'll get the background color. Now, when I run my app, I don't get---nothing happens. I get an IO. And I have to run it, then I get my background color. So, it's just like everything else. There's an intuition here that you can kind of go with. Go with your gut instinct. What's going to happen? If Store.get returns an IO, and I map over it to get the background color, who cares what's going on? It's just going to give me the JSON and I'll parse it. Whatever. But at the end, we've got to do this special runIO to kick it off. If you do runIO on the one that encloses other IOs, does it recursively run? It does not. So, every time you get back an IO, then you have t-- If it's an IO, then run it. That is correct, but monads solve that. So, functors are going to do that to you. As you use functors, you're going to be like, Oh-oh, I have a Maybe and a Maybe and a Maybe or an IO and an IO and an IO. And monads allow you to join those together so you just have one. And if you end up with that run, stop, run, stop-- So, you can combine a bunch of IOs or roll them up, then when you do runIO, it runs them all. It's basically---we'll get to it with monads. But the answer to your question is if I have more than one IO, I can make it one IO. And then I just run the one IO. So, it's almost like a Promise interface, but you can embed Promises within Promises. That's exactly what this is. It's like a Promise. You know you put your functions in your Promises, you say then do this, then do that, then do that. But Promises run immediately. This just waits. This is like lazy Promises. Beautiful, that's exactly what it is. I'm glad that you brought that up. We were going to talk about---actually I think we were going to talk about it right now, like right after this. Brian, before we leave that slide, there was a question, Why don't we need to map get over JSON.parse? Because we map getBgColor over those preferences, so that map has opened up the IO and passed the preferences into getBgColor. GetBgColor just gets the preferences right off the bat. But we had to map over it. So, it's like if that was a Maybe, you'd map over it, and the value inside the Maybe goes into the next thing. It's just like that. You don't really think about it as having a function inside. Don't worry about the implementation details. Just think about it as, This will return me an IO, and I'll just eventually map over the value it returns. Does that clear that up? You open up the IO and boom!, there're the preferences waiting for you. And you just give that to getBgColor, which will parse preferences. If you get it from a store, I was just kind of showing that it's probably going to be a JSON string, so we'll parse that JSON string. Plus, there's an example that's this exact thing. Don't look at it anymore. Oh, I wanted to---last thing before we do a couple more exercises here. And that is---oh, we're running low on time. The last thing here is that email IO has an IO with a function inside it, but getValue calls this .toIO at the end. Now, the difference is getValue is a function that will return an IO of what that will do. It's basically like, Don't run this thing, return me an IO of this thing. The top one just does it off the bat. The reason we have that bottom helper here is to be able to pass arguments to the function inside the IO because if I didn't call toIO on the end, it would be very hard for me to get the selector in there. Anyway, long story short, we're going to call toIO at the end of everything instead of wrapping it in an IO. Just as a helper function, that will help us get through things without having problems with arguments.
Either/IO Exercise 1
Again, I'm going to go ahead and define this log. I wonder if this will---I think you guys will probably all---oh, you know what? Oh, you know what. Forget it. It's not going to be easy to log. Or maybe it will be. Who knows. Let's try it. Maybe you guys will get this change, maybe you won't. I don't know. JS Bin mysteries. I didn't say earlier, but I'll say it now, Either is good if you put it inside a Future when you're doing asynchronous stuff. We'll talk about Future soon. But right now, let's think of it---Future's wrap up that too, so let's just worry about synchronous versions of this when you're dealing with Either right now. Is anybody having any trouble understanding any of these? This first one, you just want to write ex1. That's exactly what we've been doing with the other ones, just with different types. Everybody has their own JS Bin URL once you start editing it, so if you have problems, we'll just put it up here and play with it. Chat or raise a hand if you have problems. We'll drop it on the screen up here and work with you on it. And hundreds of people will love you for it. So, we're just going to go through these quickly because we're running out of time. And the main---it's the same stuff. And I know it might not look like the same stuff, but let's just talk about it for a second. So, what we want to do is checkActive and showWelcome if they're active. So, checkActive is going to return us a Right or a Left depending on if they're active. So, Gary's not active but Theresa is active down here in the tests. So, what I'm going to do is basically we're going to checkActive. We'll compose that with showWelcome. But, by golly, checkActive returns a Right or a Left, we can't just call showWelcome, which expects a user. So, we have to map over that. And at this point, you should be like, Oh, this is the same thing we've been doing. So, run that one. So, 1's good. And it runs for Theresa but not for Gary. And if you want to prove that, I could do that. So, it's good to fail these tests just to make sure you're on the right track.
Either/IO Exercises 2, 3, and 4
For exercise 2, we basically want to do what we did up here. We want to return a Right or a Left based on the fact if our length is greater than 3. This is like a validation. If our length is greater than 3, we will return a Right. Otherwise, we will return a Left. So, we're going to return a Right or a Left, and the Left says it should have this message in it so my test passed. That part's done. And the Right says it should have x in it. Okay, that's easy enough. Now what do I want to do? I want to check if the length is greater than 3, I'm going to return a Right. So, if x is the length is greater than 3. You have to put it in parentheses or the precedence is weird. Sorry about that one. We could have done it if-else too. But there we go. Exercise 2 passed. So, all we did was write a validation that returned a Right or a Left, which is going to affect the rest of our whole app from this point on. If they're not valid, it won't run. Now, exercise 3, this one is pretty fun. We're going to save the user, and we'll see a console.log("SAVED"). Otherwise, we won't. I don't know if this one's a weird one or not. So, we're going to call this string or we'll call that. Oh, it's our function above. We want to use exercise 2 that we wrote ourselves to see if their length is greater than 3 or not. So, save them if it's greater than 3. That's easy enough. We'll map(save) over ex2. And the reason that is is because ex2 will return us a Right or Left if it's greater than 3, and then we'll save them or we won't based on if that returned a Right or a Left. So, we just kind of extended our computation to save if the validation was---we've done a validation here, and it'll save or it won't. I know I'm going really fast over these. But are you guys kind of hanging in there, or are you like, God, what's going on? How are we feeling at this point? I'm going really fast to try to make up some time. If someone's like, Please help me, I will help you. I'm not going to just run through it. So, let me know. Exercise 4 just asks us to get the text. What is that going to do? Oh, this one's interesting. This is IO. So, what do we do for IO? Let's spend a little more time on this one. We are going to---it's going to do this querySelector, getValue, we'll do the querySelector, and it'll return me the value. So, I want to get the value from this test input, and then I want to strip the spaces. Well, just like always, we'll get our value, and that returns us an IO. So, I have to map stripSpaces over it. At this point, you should feel like, This guy's just doing the same junk over and over again. There it is. But the beauty in that is map is so generic that the type of container you put it in, you get different behaviors, and it's very useful. So, now this is a pure function that I have to runIO. If I don't ever run it, I never get anything out. Totally fine. I get an IO with this whatever io.val inside of it. It's kind of weird. So, it's purely reading from that, and we have to tell it, Man, you've got to runIO to actually kick this thing off, and that's cool because that means we could write our full app without worrying about anything. And it'll just return---we could just map and do other stuff to it, and it's totally fine until the end where we kick it off. We push those dominoes.
Either/IO Exercise 5 and 6
Finally, this is the last example. So, look at this one. So, this one's-- just like the others I think. We want to get the Href, which is a secret IO action that's going to get the href, and then get the protocol, we'll just split it and grab the first part of that. So, I think just like every other one, we're just going to map(getProtocol) and getHref. Run, and 5 works. This is just another example of the same thing. So, is this getting boring, or is this still interesting? Because if it's still interesting, I want to talk about it. I feel like it should be boring at this point though. We've got this IO, we just map over it, done. Mims W. is wondering just for clarification sake that implementation of left.map seems to not do anything. Is that right? If whatever function you put on it, Left just churns itself back out basically. Just ignores it. That's why this error message bubbles up the rest of your app and shows up at the end. It says, Oh, I stopped running your app because of this. In exercise 6, this one's a little bit trickier. Let's just quickly run through it. Christian's got it. We've got a Maybe here, and we've got an IO here. So, let's talk about this one. This one's an interesting one. So, we want to return the Maybe(email) from the cached user. We put the user in local storage, we JSON.stringify this user, and so what we want to do is get him out of storage and get his email because it's George, and he's a guy. So, let's start with compose. You kind of figured that out by this point. So, we'll getCache, and that returns us an IO, so we can't use it yet. We have to map over it. So, what are we going to map over it? We want to get this email, right? So, we'll get('email'), and most of you might have done that because every other example is exactly like that. But there's a problem. Because---well, first of all, it's JSON.stringified so you can't get the email on it. So, we want to compose that. So, let's compose that. Let's say getStringEmail is the composition of get('email') and JSON.parse. So, we'll map getStringEmail. But even then, it doesn't work. And the reason for that is that Maybe comes back. It's an IO of a Maybe. So, I have to map the IO, then map the Maybe to get to the email. That's kind of wild. Let's look at that. I got an Unexpected identifier. But if I just did this, if I just mapped getStringEmail, getStringEmail's going to get a Maybe given to it because that's what's inside the IO is a Maybe. And we don't want to JSON.parse a Maybe. It'll just be like, What? So, we actually have to map(map), map over the IO to map over the Maybe. We're composing functors. We have functors and functors, context and context. If I wanted to write that, I could say this thing's type is the composition of Maybe and IO. That's actually an IO of a Maybe? Maybe of IO. Put it in an IO, then put it in a Maybe. So, it's an IO and a Maybe. And I can't map over that. I have to map(map) because I have to get into the IO then get into the Maybe. IO takes care of the first map. It says, Okay, I'll run you. Maybe says, Okay, I'll run you if you're there. So, we've composed behaviors just like---I want you guys to think about this for a second. The first part of this class was about composing little bits of functions and data to make an app up. Now, we're composing full-on null checks and input like pure IO and error handling. We're about to compose more of these behaviors, but I hope you can see that we're actually composing things that don't feel like they could possibly compose, like a null check, like if null, well, how does that compose with anything? But, boom!, it does. So, lazy computations, null checks, error handling, etc. Jason says I don't have to map(map), or is he talking to somebody else. Luke H. is saying when I have to map(map) or map(map(map), I'm going to question the usefulness of what this is. And I'll talk about---you can solve that. There're monad transformers, and you can use lenses. There're all sorts of ways to do that. You're not going to be stuck with map(map) all the time. But what I'm saying is---the point is that you're composing behaviors. Is that what flatMap would be? Almost, flatMap works exactly that way if they're the same type. So, same functor. And we'll talk about that next. But first we have to learn two more functors, and we'll go quick. I hope this example wasn't too quick, but for the most part, it's just compose this function and map over it, compose this function and map over it, etc., and so on. The only difference is IO has this special thing to run it. And this one has an IO with a Maybe in it, which is the same junk, compose this function and map(map) over it. (laughs)
Other Functors
We'll go over these pretty quick. It's about the same as every single thing else that we've been looking at. But there are other functors out there. We've seen null checks and IO and error handling with Either. We have EventStreams, which could be---you could translate a list of---you know how you can addEventListener? You can't compose addEventListener. That's doesn't do anything. It's like, Run this function every time. well, run this function every time this thing happens, that sounds like a map to me. What if I compose an EventStream of every click on the screen that can map over that. It's just the same as addEventListener and run this function, but I get an EventStream out of it, and I can keep composing that. So, let's look at how that works. We're going to use Bacon here, which is probably the most popular except for RxJS works similarly anyway, different names. Bacon just has its own weird API to get click events and whatnot. That's one way to do it. This way is kind of neat because I don't have to get the function, or I don't have to get the element first. I can just say, Get all the clicks on the document. And then we just say, Yeah, hey, anytime there's a click on the document, return me the Id with the # in front of it. So, I get an EventStream of strings. Now, that top line where we actually map the turning into an Id, that would be the same as addEventListener, document click, run this function, make an Id. But what we get with streams is a tangible object just like we had with all the other functors that we can compose and map over. So, now we have this EventStream of strings because it transformed those clicks into Id's. And it's all lazy. Nothing happens until we call onValue down at the bottom, which kicks off a subscription, really starts listening, and will run that for every time that happens. Let's look at another example. So, just like we were turning---we were mapping those things into Id's, now we're actually running querySelectors on it. We can map again because it's still just an EventStream, that's what that _s is supposed to kind of denote. So, we've got this EventStream of Id's coming through, and we can find them. Now we've got an EventStream of elements. So, whenever you click something, I get an element coming through. And down at the bottom, I subscribe to it with onValue, and I'll just alert the inner HTML of the element. And that's pretty neat! Notice that I could have just mapped once and got the Id selector, but I'm mapping twice. It's like a composition. We talked about that a little bit. Map and map and map is like a kind of composition over functors. And you can kind of choose which way to do it, but just know that every time you map over it, you get a new EventStream. And here's kind of a crazier example. This is the last one I'll show before we go onto the next. Maybe there's one more. The point of this one is to show that I can keep mapping over this EventStream, and it just keeps transforming the EventStream. So, we've got these hovers, and I've got a stream of hovers, and we'll get their Id's, and we'll turn it into querySelectors, just like we did in the other example. And then we're going to actually get their postid from the elements, and then we'll get the API call. And none of it happens. It's all totally pure. So, you can do crazy stuff like that. It's kind of the reactive FRP stuff everybody's talking about. All this stuff is kind of wired together that whenever some hover happens, the rest of it kicks off, except nothing happens until I call onValue. Brian, there's a question in chat--What is this Bacon library that comes from EventTarget. Oh, it's just an EventStream library. There's RxJS, Bacon, there's a bunch of libraries out there that you can look up that have these things. It's basically an event delegation and all of the events roll up into a stream. Yeah, but it's an object so you can do crazy stuff with it. But you can map over every event from the page that's within the document. We'll be going over some of the libraries, and you'll be able to take the GitHub code and actually look at all of these examples and all the stuff. So, don't get too hung up on like, Where is that? I need that right now. You'll get it. It's cool. Notice I snuck a little Future in there. We're about to talk about that. So, we get the ProductById. The thing that I thought was important to show is that this whole app won't run until the hover happens, and I finally subscribe, well, I just say alert, so it's just going to alert whatever post or product comes through or whatever. Moving on. We're going to do examples of EventStreams and Futures in a minute, and it's going to feel exactly like the other functors, so no big deal. I'll just do them because we've got to hurry. Sorry. So, we have this Future of an eventual value. This is just like a Promise, but it's lazy. So, it's just like IO in that way. It's almost---it's got IO rolled into it, because when are you going to get some Future value and it's not going to be IO? You're going to read a file or make an API call. I guess there's one case where you have a long-running calculation, but nine times out of ten, it's a Future value that will be some side effect, and it'll come back eventually with a value. So, just like EventStreams, just like IOs, these things build up computations, and they don't run until you kick it off with this fork method. So, let's look at that. So, if I do an http.get of posts, that'll return me a Future. And I'll just map(makeHtml with the post from the Future. So, this is the alternative to a callback. And Promises work the exact same way. If this returned a Promise, I could map(makeHtml over the value inside of that. So, it's just like Then on a Promise. The idea is that we map this function over the eventual result. Now, this fork thing is where it gets weird. The whole app is going to look like this stuff above here. But the very end of the line is going to have this fork of whether you're either going to have an error or whatever the result of this makeHtml, which is the page. That's an important concept because Futures will fail. There's a good chance your Future's going to fail, whether it's an API call or reading a file that might not be there or whatever else. So, they've kind of rolled IO and Either into this one type kind of where you're either going to have your error or your future value, and it's all lazy. I have to call fork. It's just like runIO. I say like, Oh, now do it. Let's look at the next one. Here, we've got our---same idea, we're going to get our post, which is going to return us a Future. And then we're going to map createPage over it, and we're going to get the title of the post, make some HTML, whatever, keep mapping things, doing whatever. And, sure enough, down at the bottom, we actually do some effect with it. We're going to put it on the page. You can't just get the value now. It's not going to be there. You have to map things over it and eventually use it. And that's an important concept with any functor is that at the end of the line, this is the impure code. All this stuff up above, that's your app, and it's all pure, and it's all wonderful, and you can just treat it like it's there. Boom!, you finally call it, and it's either going to blow up or now. People are confused where the Future is. Http.get is going to return a future. I didn't actually explicitly make a Future here because it's a little bit weird to look at, and I will guarantee you nine times out of ten, even if you're using Promises or whatever, the HTTP library or the readFile or whatever you're using is going to return you a Promise or a Future or whatever. So, you don't have to really be making them all the time yourself. So, just know that this returned a Future so we have to map over it. What else would we do? They're wondering where is the http.get library. Where is that coming from. You guys, it's not real. Hang on tight. We're going to do the demo soon. That's why I'm running through this really quickly is because we're going go to use the demo, and you'll see a real Future and a real HTTP. And here in the JS Bin right here, we're going to---I'm just going to fake it with a set timeout, but you can go look at that. I just wanted to point out this last one, readFile, if you're in Node, this stuff is awesome. If you're in Node, everything is asynchronous all the time, and it's a pain in the butt. And this stuff where you're saying, Hey, read the file and then map over it, makes it so much simpler than this callback hell. This solves that for you. And you can do a lot more than just combine them. And they adhere to the functor, and we'll see monoid, and applicative, and monad interface as well. So, you can do all sorts of crazy stuff with these things. Isn't in Node there an easy utility that you can wrap around native functions and get back a new function that returns a functor? Oftentimes, Promise libraries give you that. You can probably do the same thing with Futures. The difference between the Promise and the Future, the Promise---it's like you have to run it at the end. And that's this fork or error function or success function.
Other Functors: Exercise 1-5
This is just like everything else. It's starting to get boring at this point, but I needed you guys to see all the cool stuff you can do with Futures. So, we're just going to get the post and map the title, get('title'). Run that, it works. So, what's going to happen if I show this little log here. I keep defining this. So, I can actually---let me just do log here so I can map log over that. And see how it's logging this object? It doesn't log it right off the bat. It says Start 1 and Start 2 and then eventually it logs it. So, it gets passed this and then comes back and does it. Because it's waiting for when this is ready to log that. It just says delay this for when you're here. And I get the post, and it will show up there. So, this is incredibly useful for API calls and such to where you're actually getting things and then mapping over them without having to do all this callback stuff. So, we'll get the title here and get back to normal. So, we get the title from the post. Everything is good. Exercise 2, same old, same old. I'm just going to---it says use 1 to extend the computation. That's a good point that I wanted to show. We'll map render over that. I'm just doing this pattern over and over again basically. But the idea is that this thing gets the post's title. And it doesn't do anything but return me a Future that I can eventually fork. So, if I want to just extend it, I can. So, if you have a widget on a page, and that page is going to go make an API call to JSON P, whatever, and then come back with stuff, and then maybe we do some IO and whatever. If your widget returns me an IO of a Future, I could take your widget and extend it and build upon it without ever running or forking it. That would be awesome if more people knew how to do that. Because I would be able to make bigger better widgets out of your widgets. But people don't do that. They're like, I'm just going to run and blow up your page. So, the idea is you're making these lazy computations that can be extended. And so we're extending our exercise 1 to put that title in a div. We did both of these. And then, finally, the third one. These are interesting just to see over in our output here, I have to click it to run it. So, let's go here. It says click the output and then click the div to run the test. And all we're saying is we want to return a stream of the innerHTML. So, we've got these clicks. And I want to return a stream of the innerHTML. Well, I got the e.target.innerHTML. What do I have to do? What don't I just get the e or the target and we'll get the---we'll call, whoops, this is backwards. Get the target and then---I don't think I have a nice innerHTML function, do I? So, we'll probably just have to---what I'll do here instead because I don't have a nice little function is to say clicks.map, and I'll have it click our event, and I'm just going to grab the target.innerHTML. Don't forget to return. And now I have the innerHTML stream. So, whenever I do that, boom! Let me run it. It still works. And the last thing is the exercise 4. Exercise 5 is bonus. You guys can play with that later. Oh, no, I have to do exercise 5. Let's skip 4 and do 5. This video is just going to be me mumbling to myself during these examples. Why can't we do 4. We can do 4, it's just we're running low on time, and it's the same as this one above. So, 5 is a crazy one, you guys. Let's look at this. So, the example is to say get me---use safeGet, we've seen safeGet in other examples. It says we're going to safely get the address, which returns us a Maybe address, and then we want to safely get the street. So, I have to get the address, then the street, oh, and then its name. I actually have to get the street name. It's going to be intense. Okay, why not? So, I'll safeGet the address. That's great. What do I have here right now after I safely got the address? I have a Maybe of this object. Are you guys seeing that? Once I safeGet the address, I have a Maybe of the address. No big deal, we've see that a million times. But when I safeGet---or I have to map over that. We have to map over our Maybe. And if I safeGet the street, what do I have here? Let's actually look at this. Let's go play with this. The expected Maybe(Maybe(Walnut St)), and I've got a Maybe(Maybe( and I mapped over that Maybe, which returned me another Maybe, so I have a Maybe and a Maybe. This is ridiculous. We can't really squash these guys yet. We don't have any tools to make it one Maybe, like flatten it. So, what I want to do is I've got my safeGet street, and now I could do my map(Walnut)--- or safeGet, our name, there we go. We've got this going. Oh, wait, no, that's a Maybe and a Maybe. So, I have to map(map) that. That's fine. We're getting there. We run it. I actually think I wrote the test wrong you guys. Sorry about that. It's got three Maybes to get the right answer. So, it should be Maybe(Maybe(Maybe)). I'll fix that one. It was bound to happen. I'm not perfect. How do you solve that? What is going on here? We've got Maybe(Maybe(Maybe)). We've got this pattern of like map, then map(map), and then this is super weird and gross. What's happening is we're---we've got three null checks going on. I don't know why this isn't equal. That's also a test problem. But we have three null checks going on. And we can see that blatantly. If there are three possible things that could be null, I'm getting three possible Maybes nested inside each other. This is a sequence. This is nesting---if I said if not null, do this. If not null, do that. If not null, do that, and keep nesting this giant gross nested null check. That's what that is. That is a monad.
Functor Laws and Properties
We've got these---so you've seen Promise, Maybe, EventStream. We've got Future, IO, all sorts of them. It's just map. I don't know why people need to make up their own names and APIs. It's kind of frustrating, especially when map has laws and intuitions and whole mathematics behind it. And people are like, I don't know, subscribe! And so there is a way to do it that will guarantee that your library composes with other libraries. And there's a way to do it to where everybody doesn't have to read your documentation. They just kind of know. Didn't you guys just kind of know how to use EventStream and IO eventually after you just see the map? Hey, if it's returning something, you just map over it. That's all it is. So, you feel it. You feel---you know what's going to happen. It's going to return me a new IO with the transformed stuff, or whatever. These laws and properties are actually useful in your day-to-day code. There's a ton of them. Functors have laws. These are laws that you have to obey when you're making a functor. And you know that identity and composition---I don't know if you remember but there's this thing about categories and compose and category theory. Those are the same laws just in a different kind of way. The others have Left and Right identity and associativity. This is kind of like the functor playing off of those laws' laws. So, it's connected to compose in some way, and we'll learn more about that if I have time. Somebody get me a Red Bull. Here's just a quick look at something interesting with the laws. I know that---this diagram on the left is just a cute drawing from a great blog post that kind of shows what a more formal category theory diagram might be. If I have a functor, or if I have just a normal function from a to b, and those can be any type. So, in this case, that's reverse, string to string. A is string, b is string. They don't have to be the same type. That's why---it just happens to be. Now, if I run reverse on bingo and then I put it in my array, I get ognib. And if I first put it in my array, then fmapf over it---fmapf is map, I get---functor map, whatever, I get the same result. Both take me to the same place, a to t(b). T is our functor, b is inside it. So, I get our array of string inside of it. Very interesting stuff. It's there for you to learn. They're in the slides. You can look back at them. Here's another really interesting idea. We could actually do that composition and---you don't have---if you do a map(map), that's the exact same thing as mapping composing. If I go back to this law at the bottom, it says if I compose(map(map)), that's the same as map(compose). And if I see that, literally see that line in the code, I could always change it. You guys were writing that a minute a go--compose(map) partially applied with f. I could just do map(compose) partially applied. So, that is actually useful. It's a design, it's like right there in front of your face. So, the bottom one is way more efficient. If I have an array of a billion things, and I map over it, and then I map over it, that's going to be really inefficient. But I know I can always must map the composition over it once. Natural transformations--tidbit of information. I shouldn't waste or time on it, but essentially you can take one functor to another functor, if anybody cares about a natural transformation. A lot of category theory cares about that. You can turn a Maybe into a list just by making it a singleton list or a list with nothing in it. So, that's a natural transformation from a Maybe to a list. And there're more laws and more diagrams, and it's really just connect the dots. Because remember the dots? They were just like, Hey, this function goes to that dot, and that function goes to that dot. I can cut out the middleman and just go straight to the other dot. I don't have to worry about all the intermediate steps. This is that kind of stuff but with contexts and wrappers and mapping. And it all relates. I'm not going to go over---it's not that hard. You can look back at the examples. It's all very easy stuff, but once you build up and just know these simples truths. Okay, we're going to do a fun game to break it up. Everybody's like, Yay! So, if I want to make an API call with an Id and possibly retrieve a post, what functors might be part of that? Does anybody have an idea? Just shout'em out. There's a Maybe. And what about the API calls? IO's good. You could also do a Future. I would have chosen Future because Future has IO in it. But if it's an asynchronous call, IO would've been perfect. I've got a Future of Maybe. These aren't right. You can do different things. You could have possibly retrieved a post, or you could do an Either with an error message. I'm not saying this is the right answer. I'm just saying, it's cool, you can mix and match these cards. How about I click a navigation link and insert the corresponding HTML onto the page. So, I'm going to click a link, and then we're going to set the HTML---we're going to build a page. EventStream, that's good. Anybody else? EventStream and IO. There you go. It's putting it on the page is definitely an IO. EventStream is lazy. Nothing happens until you subscribe. But when you do subscribe, you want to do some IO, right? Or you don't care. That's fine too. If you don't want your thing to be extended and that's the end of the line, just put it on the page, don't worry about it. One last one. Hey, we want to submit a signup form and return errors or make an API call that will create a user. Did I word that correctly? I like that. The EventStream could be the submits on the form, and then the Either could be the---I think I did just a---hey, I did that. We've got an EventStream of an Either of a Future. And now notice these are different types, so we're going to have to map(map(map)) that. And it has to be kind of clear because you're saying, Listen, guys, I have three different functors, three different contexts here that are nested. Now, we talked a little bit briefly about how you don't have to map(map(map)). You can define a lens to do that. I don't have time to go into them, but it's pretty great that you can map three-deep in contexts.
Monads
So, this is the introduction to monads, and we will do this in the next 10 minutes, so that's pretty great. How's everybody feeling? Should we take a 10-minute break? 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. You guys see that? We've got an a and now we have an F(a). It basically takes an a, puts it in something whether that's an Identity or a Maybe or something. That's what of does. Why not just the constructor function?, you might ask. Like, I have Maybe, why don't I just put it in a Maybe? This function of is also known as pure or return or unit or point. It's a pointed functor, a 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 like put it in the box, why not? Well, if I want to put the Future of this function, the future of dubstep matches 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, but we want to put a function inside it 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 some crazy observable or something, and I want to put something in it, this is better than calling the observable. Or promise, for instance, if I want to put a 2 in a promise, if I said Promise(2), 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. And we'll use that with monads. Just take it in. 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. So, the types of mjoin take something with a container of a container of a and just gives 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 of b and a and then returns you a container of b or M, a container of a to a container of b. 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 because you can define one function in terms of the other, which is pretty neat. Oh, I think I still have that aka from the last one. That's a mistake. So, if I call mjoin on a (Container(Container)2))), I don't have the answer. I'll get a Container(2). I don't have two Containers anymore. It'll flatten them like a list. Not bad, right? So, a (Container(Container(2))), and I mjoin it, I'll just get a Container(2). So, let's 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 going to 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. 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 the second Maybe, and then render the template. This is common. If you start combining contexts and composing contexts, this will happen to you. I'm sorry. You have to give up there. 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'll take a Maybe(Maybe) to just a Maybe. So, you guys are familiar with list flatten, right? It's like you've got lists and lists and lists, and you just flatten them into one list. Here we have a Maybe and a Maybe, flatten to 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. You're just like, Oh, no, I'm nested. Mjoin, done! So, that's cool. Let's see another example. We've got two IOs. This getSearchTerm was going to get it out of location, so we want to make sure it's safe. It's an IO, so we don't want to 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, we get two IOs going on. We get our search term, map that. We've got an IO(IO), right? If I just---in here, I have to map(runIO) over the first one, which is hilarious, and I end up with another IO that I have to run, which is weird. So, here, we just mjoin it. Done! Someone had a question about Promises. He's saying they don't resolve immediately. But they broaden immediately. So, the differentiation between IO and Promise is that IO doesn't run immediately where Promises run immediately. That's fair, I wasn't careful with my words. And Futures here are like an IO-ish Promise where it doesn't just start executing and eventually run. But you're right to clarify. So, we just drop the mjoin in and we're good. And if everybody knows how to do that, you're using monads--- now, I have to 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 going to go to Maybe and say, Tell me how to join you. I don't know how to join you. So, you have to---these things have to be monads as well, right? Well, luckily, all the types we saw were and just about every type you'll be working with kind of is. But maybe not Either, yeah, it is. It's a monad. Brian, there's a question. Mims is trying to clarify. So, is mjoin a monad or is monad a result? 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 a join method. But having them joined just recursively calls map? Sort of. If you think about Maybe, it would say, Oh, I have .val, and if it's not null, I'll pass this val and then the next. It takes care of it for you. You just tell Maybe, I've got a Maybe and a Maybe, and I just want one Maybe. Can you tell me how to safely do your null stuff and make sure there's only one? So, in that other example where we had triple? It'll take care of that too? That only happens---you have to mjoin twice. Now, that's not a big deal because there're ways to compose monads, and we'll see that. So, you're not typically ever mjoining twice. We're wondering what's the point of an of method? What does it mean to have an of method. It is almost exactly like a constructor. But it puts a value into the context, not necessarily what the constructor's 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. Of lets you put anything in there. I could put the number 2 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. That's what the of does. And this mjoin tells it how to---if I have an EventStream in an EventStream, let's make it one. Would you ever use of if you had an unknown functor? You're like, I don't know what this was. I don't know what constructor you used to make another one, but I can make a value. That's a great question. In the Fantasyland spec, which is online on GitHub that we've been all going off of, it's basically the same spec, it's the category theory stuff, it's just they did it for JavaScript, and it says of should be on your prototype as well as your constructor. 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 like a dynamic way to put it in whatever context I've got. I don't need to hardcode my type into this computation. I can get some value and call of on it, which is really neat. 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 a Future, switch your Future to a different library, it still works. Or switch it to Maybe, and 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. So, let's look at one or two more examples and get into the code. It's not that hard, it's just drop mjoin in there. So, it's not that crazy. Functors were the hardest thing in the class, and we got through it. You guys are alive. Here is one where we read a file, and then we upload it to the server with this fictional httpGet, which will return us a Future because it's going to the server. So, it reads the file, goes to the server, we've got a Future and 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 a Future(Future). 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 we'll join those two Futures. Then we'll send to the server and, again, join those two Futures. 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. And this is three nested callbacks. If you looked at a Node app, it would be this disgusting 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. Two questions on this site, Brian. Robby V. is asking, Can you mjoin functors of a different type, like a Maybe and an IO. You cannot. That is a---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. Or, better yet, use the lens library to just define a double map. But that's kind of beyond this class. And Daniel B. is asking, Does fork return a Future? No, fork takes a success and error function. And it operates on a Future? Yes, it returns kind of null. It's the end of the line. We're done there. One thing to point out---did I have my code? Where's the example. Here's chain just so you know. It's just f map mjoin. Not that hard. All we have to do for f map mjoin is just chain it. 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 you just drop a replacement for f map and bind. And you'd see the same thing as flatMap. Different names, same stuff. The reason we call it chain in the Fantasyland spec is because if you do the dot syntax, it's literally chaining. This is the same thing but dot syntax versus the super sweet functional point-free style. One last thing I wanted to tell you guys before we jump into the examples. Mjoin is defined by chain(Id). It basically chains those--open up my monad, open up my container and pass the value into it. An Id just says return the value. So, that's mjoin. Whereas chain says, First add map, then mjoin. So, both of those can be defined in terms of each other. So, any monad can be defined with one of those two functions, and the other one is just defined based on the other function. Brian, before we take off, Scott S. wondered, Does Either.of take two parameters? Oh, that's totally, that's totally smart. I think it only takes one. But I don't know that answer. I don't work wither Eithers all that much so I'll just get it wrong. We're in all this futuristic stuff. 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.
Monads Exercises
Real quick, we've got some confusion here on fork. So, we're calling fork on a Future, but earlier we defined a fork function where we gave it lastly and an f and a g and an x, right? Different fork, different fork. I don't think this is my most up-to-date monad example, but that's okay. We're just going to kill this because you guys know this. Let's talk about this. So, we're going to start with that last example we just did. We had a safeGet('address') and a map(safeGet) of the street, and so we're traversing the address and the street safely. The alternative to this code is if not null, do this, and then nest it. If not null, do this. So, we've got our street, and then we want to get its name. So, we'll again (map(map(safeGet('name')))), and bing. Now, let's see if exercise 1 is unhappy about something. There you go. So, expected Maybe(Walnut St) to equal Maybe(Maybe(Maybe(Walnut St))). So, this is kind of a mess here with all these Maybes. Why don't we say at the first sign of it getting nested, we'll mjoin it. And we'll get rid of one of these maps. Let's see what we get for that. We get a Maybe(Maybe(Walnut St)), one less. We're getting there. And then we'll mjoin here to clear it all up. And there it is. It passes. We've just got one Maybe. Now, we just learned that map, mjoin, map, mjoin is just chain. Let's just call it chain. There we go. And there it is. We're just chaining through. Let's do another one. We've got two IOs. We'll just compose getHref with mapping pure log, and we will mjoin that. That's all we've got to do because we just put it in another IO. And that passes that. Let's see if we can just call a chain here instead. There we go. And the last one says we've got two Futures. We're just going to basically get the post. And one thing to note about monads is we are nesting calls, so we're saying, First, get the post. Then get the comments. So, first get the post, then get the comments. Actually, getComments takes an Id, so we'll get the Id first. And then, yeah, that's it. And then we just mjoin them. Hey, Brian, there's a request to slow down a little bit. Seeing as it's 4:11 and we still have a real-life example to do, and this is the last example. And I broke it anyway here. The point of this is you've been seeing map this whole time. We've been seeing what map is doing. We're essentially just going inside the Future, and now we're running and we're getting another Future inside that Future, and now we have a Future(Future). So, anytime we have a Future(Future), we mjoin them, and we're all good. And I think this will actually work, even though it shouldn't be. Yeah, it works. It should be getting the---mapping the address over the post, but that's fine. So, there's not really much to it besides dropping mjoin in at the end. Or, instead of map mjoin, whenever I see that, I can call chain. And all is well. When reading, can I essentially ignore the chains and mjoins? What they're telling you is saying like, We're going to first get the post, then we're going to get the comments. Chain is telling you that. You're not just like, Get the post and get the comments. I don't know. It's telling you what's going to happen. But there is a way to compose that will compose two monadic actions together. And let me just finish up before we do the demo by saying, there is more to the story than I'm telling you right now. And monads have these same laws and same paths to go through. And we're going to define mcompose. Do you guys remember those category laws where we had functions, we had compose, right? And compose had left and right identity and had associativity. And we saw those functor laws that were kind of playing off of those. Well, monads form a category. They're their own type of composition, and they have their own identity operator, namely, the of part. Actually, it should be m.of in the JavaScript spec. But the idea is that you have a category of monadic stuff that has all the same laws that apply to non-monadic functions. And it's its own world, and it's got its own rules, and you can do all the same things. All those things that we could do with normal functions in compose, we can do that with monads.
The Demo
Project Setup
Okay, everybody--internet, people here. We heard all about the laws. We heard all about the categories. It's going to be a different style now. We're doing The Demo. First thing---just a couple of slides and we'll get into it. First thing, congratulations! In a way, you're pioneers because, although these techniques are well-known in other languages, especially in Haskell, bringing them into JavaScript is a new thing. And that's why you saw all the imports in the JS Bins and things because we're cobbling together the ecosystem of what we need. And things are only getting better. So, if we use this stuff for real, our input is going to go to make those libraries better. Use these things, send them more requests, send them issues, because it's only going to polish---like shaking the rocks until you polish them. It's going to be good. So, what we're using in particular, I just wanted to mention so that if you do---kind of boiling it down so if you want to try your own stuff, you can pull these in. Of course, Ramda. Baconjs is doing the stream stuff that's going to handle like making the streams when you click and type and stuff. Fantasyland is a spec. It just says, Yeah, when you're making functors, etc., etc., you could use certain names and stuff, but you should be using these names so that it all works. In particular, we're pulling the IO functor out of Fantasy-IO. There's an implementation. Our own DrBoolean here made something that wraps up some of the Fantasyland stuff so it's easier to do point-free composition on it. And we're using some stuff from Folktale. A lot of these things, if you look at Folktale versus Ramda versus whatever, there's this large overlapping---they're all trying to do the same thing. But I think that in my opinion, at least from what I've seen, Ramda's the best for most of it. And the others are good for throwing in some stuff that Ramda doesn't really have yet, like some of this functor-type stuff. You might even say the bottom three are about the part 2 of the class. You've got the function level, and then you've got the functor object level and stuff, right? Yeah, that makes sense. I guess it's fortuitous that they aligned that way. Another thing which I didn't put---this is the last slide, but we're going to go through an example that I put on my GitHub, which is begriffs/immutube, so github.com/begriffs/immutube. So, the great thing about this is that we've already---I've already run through it, and I have commits at each point. Can you go back to that? So, let's do it. When you look at immutube, actually master is like some earlier stuff, and the branch that I'm on is livecode. And I'm eventually going to merge it into master when we think it's all cool. So, what I think I should probably do in livecode is I'll have one tab open with some of these commits if we have to go over to them, like if we get kind of stumped, we can just be like, Yup, next one, we'll be fine. So, I'll leave that open as a tab. And I'll check out blank slate. We're at the blank slate, and we'll just work from here. Let's see if I have some vims and crap going. I should probably close some of that. So, there's some stuff---there's actually some crud in here you don't have to look at from previous experiments. This was originally going to be a very different app, and we decided on something a little more self-contained. So, index is going to load some stuff. Or the real action happens---well, this also is in---main, no, this isn't it. Main just loads the stuff we need with Bower and crap, so we pull all the libraries together, and it eventually runs our app. And our app is just inside of app.js. So, this is where the good stuff happens. I'll start up the thing and show you what the interface look like, and we'll discuss what we want this thing to do. So, I'll go back to my terminal. Oh, it's already running. There're a lot of tabs going on. Just to make sure we're all reset. So, we'll go to localhost 8000. And there's a lonely input box, and I type into it and nothing happens. Yeah we need the sad trombone. Let's look at what the page is made out of because this is the stuff we're going to be attaching to. We have our input. It's called search. We have a ul with nothing in it, results. And then another spot that's kind of just hanging out waiting called player. So, the end goal of this example is you type in the box, and as you're live typing, it's bringing up YouTube search results of videos that match your stuff. And when you click one of the results, it brings up a YouTube player on the right. Daniel B. is asking if there's a server site to download or if it's all just front-end stuff. It's all just front-end. The reason I started a server was just so that when I made cross-domain requests---I wasn't in file:: versus HTTP:: and Chrome freaks out security-wise about that. So, I just---I'm just running index.html through--- It's PHP to---it doesn't matter what server's running? Just something that serves it. In the ReadMe of the project, it says how to clone it down, how to install dependencies, and how to run this little server so you can be up and running the same way.
Demo Part 1
I think we should probably start---there're all kinds of things we could do, like doing the API calls and stuff. But it might be easiest to start by making it so when I type in this box, we can just log it and we can see it in the console, see that something's happening. So, what's cool about this pairing bit is that I don't always have all these things that took my time, like exactly what this EventStream is called--bacon.fromEventStream or something. So, I'll be discussing it, and you and Brian are going to be like, Yeah, try this, and so we're going to learn the thought process. So, I'm trying---we could start with pure and impure things. A pure thing would be like just attaching an EventStream to something. You're not really running it yet, it's just there if you ever wanted it. So, it's still pure. So, we can say var. We're going to get hold of that text field, though. We should first grab the search I think. So, we could say getDom or something? And what I like to do for my own sanity in a language with strong typing, it will dot your i's and cross your t's. You're just trying to do something that makes no sense, but I add comments for myself in JavaScript because, otherwise, I get really confused. So, I'll say getDom. What does it do? It's going to take a selector, I guess this is just a string, and it's going to go to an IO of a DOM-like element. And this is all just kind of like pseudo code. So, here's a function. Actually, I wonder if we need that. If we can just take---because I've included jQuery, so I could toIO on jQuery things. I could add $. to toIO. Done and done. Now we can get things in IOs. So, we want another thing that's going to attach an EventStream---so, there're going to be different kinds of streams that we can get. We could maybe call it a ClickStream, and you give the ClickStream what is it that you're going to get the clicks from. So, it's going to be kind of similar. We want to get the search text I think right now. I'm thinking just like the events, like something happened, some DOM thing clicked---I mean, I said click but I meant typed, keypress. That's what four hours of sleep will do to you. So, let's get a keypress, var keypressStream. And that's also---let's put our docs first. I like to think of it that way. What is this documentation thing you keep talking about? It was hard to write it, it should be hard to read. So, keypressStream, that's going to take, once again, just a string of selector, and it's going to go to an EventString of---I guess I'll call DomEvent. Like I said, it's just kind of imaginary, but it's enough to suggest to me. Oh, wait, actually, it's probably going to be an IO. No, let's do it this way. This doesn't take a stream, it takes an already-gotten IO thing because if I was like, Find the DOM thing here, I'd have to be inside IO too, and I don't like to be inside all those things if I can help it. So, this will be a piece of DOM--DOM or element maybe. Now, oh wait, the way Bacon's EventStream works, the order of its arguments, like the order of everything in this world, is kind of awkward for currying. So, we can make our own wrapper for it. I could call it listen. And what would be a good way? We know the type---we always think, when we're trying to curry---when we want to make the order for currying, we want the things that we know to come first and the things that we don't know to come next. So, I say, what do I know? I know the event type that I want. What I don't know is the DOM, the element that we're going to apply it to. So, that will be probably, let's see, Bacon.eventStreamFrom or something? Bacon.fromEventTarget. And it's going to be the element of the type. I've got to return it. I should probably use a semicolon. There's a useful utility called Flip that works for this exact thing. Do we have it? We learn about this in curry as well. Is Flip curry? I could do Flip of-- I don't think Flip curries the function. So, you probably want to just leave it and curry it. I could curry Flip. I don't think we should be messing with Flip right now. I just think it's a cool thing. So, now we can listen to things. Now, a keypressStream---oh, this isn't bad. You still have a flip up there. I think we should curry it though. You're totally right. I'm so used to things being auto-curry, it's just like, Hey. Usually a rule of thumb for me is if I ever have more than one argument, I just curry it. This guy actually probably doesn't even have to have the bindTheGlue variable here. It's probably just a listen('click') at this point. Why am I thinking about the clicks and the licks. We need to think of the key. Which is the best, keydown would be like weird things like Ctrl and Shift, so probably keyup or keypress. I don't know, let's try keyup. Now you've got a stream. So, in our impure stuff, let's do something. Let's---let's see, we want to find the element and kind of compose it in here and make it log. We have a handy log, cool. The commit I started with has all this good stuff--compose, map, log, fork, okay. So, let's think through it. SetHtml is not too IO, but that's fine. We'll worry about that later. So, there's going to be a compose. The final thing will be a log, but it'll probably be like a map of log if we're going to be inside of stuff. And we're going to be doing---we're going to get the---oh, wait a second. Is map going to work with EventStream just like anything will? I don't have to be on event or anything, it's just map, like any functor? Well, you want to run IO because---well, let's go ahead and get the DOM and then figure out what we're doing with it. So, that would be an IO. And then if you log that, it'll just be the search---if you map(log), that'll be an IO of whatever log returns. I wanted to get a keypressStream of that stuff. So, we find it, we get the stream, and now what I'm wondering is if map is going to be called on the events in the stream because it's a functor like any other functor. I think first things first is we need to map keypressStream over getDom because getDom's an IO. And now we have an IO of an EventStream DOM. So, now we have to map(map(log)). No, actually, I'm sorry, we don't have to map---yeah, yeah, map(map(log)). I don't remember if we were able to move that in our final thing or if we had the nasty map(map). Well, at this point, we're impure, and so the compose is kind of silly because we have our data at this point. We have our search, right? So, why are we going through all the trouble of trying to be point free when we have our points. So, I think we should just run stuff. So, I'd just getDom.map(keypressStream). And then we'll run IO. So, that gives us, that'll actually get the search thing, and then give us a keypressStream. So, after .runIO, just do .onValue. That's what I was wondering, whether---could I have done, theoretically done map here or will only onValue work because the way the library is running? Map will just keep returning pure streams until you call onValue. Nothing will ever happen until onValue is called. Oh, isn't that a jQuery? Oh, you know, we've got to do that extend function here. Remember we hit that last time we tried this? We built this before, you guys, secretly. Yeah, we can jump to the conclusion if this takes too long. But, at the very top line, extend the---IO extend function. Bring an IO and extend. If we want IO, we're going to have to ask for it. We extend functions to call toIO at the end because it's a pain in the butt with arguments otherwise. Now, let's go to the console. So, now the events are coming through. So, I like to think of it as like you get a stream of one kind, you can intercept it before anything has ever---just purely, before anything has happened and make a derivative stream of another kind. What if our stream was now the actual key that was pressed, like grabbing that out of there? So, let's see, how do we find that? It's in the event.something or other. Is it eventKeycode? No, I want the actual deal if possible. I think we should get the target's value because we want the whole search value of the search box. We don't really care what key was pressed. Maybe stream of whatever's in the search at the time the event happened. So, at that point, it's just .target. There's a lot. Oh nice! --
Demo Part 2
So, we've got a keypress stream. We can make a new stream that's based off of it. So, I just want to point out that that bottom line's a little gnarly because you have an IO kicked off, and then it returns an EventStream, which we're going to subscribe to. But that's okay. Because what that does---everything above that line that starts our app that actually runs our app is going to pretty much return that type, and it's going to be curried and easy to work with and lots of fun. But that kickoff line's a little crazy because it's like, Hey, here's all the stuff we're honest about that's going to happen in our site, or in our app, and now you have to deal with it and open it up and run it and make sure that this all comes out. But the benefits of working in your app, that pure part, is awesome. Between pure and impure, it's really nice. We're writing a lot of checks, and we've got a cache underneath it before we pull it up. So, let's make another stream, which is the valueStream, I guess. We'll go from I guess DOM again to EventStream of String, and that would be var valueStream =, this is going to be based off of keypressStream, and we're going to map into it I guess and extract some things from the target. Definitely the target values. Let's see, it was the value, or do you have to get the target first? And that was off of keypressStream. Oh, we're already giving it that keypressStream? I was going to make a new stream and that just replaced our final line. Cannot read property 'value" of undefined. We could put a log in here. See all the stuff that's going through. Where's an EventTarget? Yeah, put a log, and let's see what happens. We're not doing anything. There's just a syntax error or something. We've got the DOM event, which returns us an IO. We've called the valueStream, so keypressStream is going to get our search. We don't have a map in here. So, you've got to map(map). I can map the composition. Let's stick with map(map) now though. It's kind of a cool demonstration of laws. Let's see before we even get laws if we don't break the law. So, it's a whole new stream. Now, let's use the laws. We don't need to map(map) because it's always going to work with that in. I would alias that to a function since it's kind of weird to have a compose stuck in there, but maybe not. I don't know. Usually, I would just pull that out and use the little helper that says target value or something. But, I think it's totally legit to be inline. So, we're able to get values. Now, we should, for each value we get, kick off the search. This is quite a chunk. What should happen first. Oh, you know what we should do? For each one we get, build up the URL that we're going to need. So, we could have a URL stream, so that things---for each thing that we type, it's going to correspond to something, some API call that we would make. So, we could say urlStream, and once again, it's _____ DOM, it seems like. I guess we're just carrying through. EventStream, I'll say URL even though it's just a stream just to remind me. I gave myself a note because I didn't remember exactly what needed to be _____. Mims is complaining that this is hard to read. Alex suggested moving the inner composition into an actual function so that it actually reads better. So, take this compose(map) right here. Yeah, the inner compose. I would just compose(map), just that (compose(_.get('value'), _.get('target'))) would just be a function of-- We could call it-- the function could go from domEvent to value. We should call it like eventValue or something. String, I guess they're going to be strings _____. I'll leave that because we'll map that. One rule of thumb is you want to call map at the caller. You don't want to force some weird type into the function. Yeah, totally, the whole point of part one was we make pure things that take a journey somewhere. Are you totally lost right now, or are you guys kind of following? The repository will be there, and we're just ____ even though I'm not doing it by commit, that does, you're going to know the difference between them. We have a different looking project on the� We just kind of made things earlier to make sure it works. Where were we? URL stream. So, we need to see what is it that these search UL's look like. They look like that. This one---I don't know if it matters to be point free, it's just kind of a string thing and a jQuery thing so you could say. We could get all crazy with monoids, but I don't want to. We could say search for term or termUrl, and that takes a term, I guess, which is a string, and gives you another URL. So, I was promising we'd get rid of names, and there are plenty of names in here. But at least they're not, I don't know, maybe they are glue names of a different sort, but they're not like the very lowest level of glued name. We were gluing together primitives to make up our application-specific DSL that we're just kind of putting together down at the bottom. So, we'll return this. If you look, it's like q = that, and there's this extra alt=JSON, so there's like a little helper we can use for that, which is +. You give it a map of q goes to term and alt goes to JSON and makes a query string out of it. So, now we've got termUrl. Now our urlStream is going to map over the original but just turn it into that I suppose, so, compose. Are we inside a monad? Are we going to have to map a functor? We're going to get a DOM. So, I'm going to be mapping over the value stream, so I have to map again. It's a new stream. TermUrl over valueStream and then do our urlStream.
Demo Part 3
Okay, we're almost ready for the magic. Now we're going to want some kind of, let's see, it would be a Future, wouldn't it? I was thinking it would be an IO to make the call, but it would have to be an IO of Either or Maybe, which is kind of a pain, and that's what Future's supposed to wrap up, I think. We'll call this one search. Search takes a URL and gives back, man, I don't know if I call them videos or something, but they're not really. They're just like the metadata for it. What should we call it? Here's a little---actually, we can check out what this kind of looks like to get a feel for what is going on and what does our code need to do. I'll just do this. I just want to point out. There was a question of Why is this better? And it's because you can compose things. That's the bottom line. If you see the power of composition, of composing behavior, of composing functions, composing these things into bigger things. And you get laws and properties, and you're totally working against an interface. So, they're swappable, substitutable. Everything turns out to be monads. So, if I make my own library of EventStreams, I can implement this interface, and you could swap Bacon for it, and your app doesn't change. There's a ton of benefits. We're going to---at the end of our app right here, we happen to kick stuff off. Like we do stuff. But rather than actually making the program happen, we could have sent back the pure stuff. And then whoever is consuming our app could be like, I like your YouTube thing, but I'm a YouTube thing that filters out bad results or something, whatever it would be, I don't know, it's getting on top of our app and composing with it rather than running it. At each stage, you're either something pure that someone can mess with or you can be run. Maybe that's an oversimplification, but that's my intuition of it. Yeah, I like to be able to just keep building upon smaller things and keep extending it. Search goes from URL to IF, so what does this look like? Whatever each of these are called, because there's a feed that has---this is a weird gap. An interesting side note is everything in the pure part has the benefits of the pure stuff we were talking about--portability, reuse, parallelism, memoization, all that stuff. So, entry--does entry live inside anything? It lives inside of feed. You know what I could do? I could say search goes from URL results, just so we can do the API call. In fact, it will actually be a Future of results, Future of JSON, let's say that. We may not need to do a function. So, what are we going to do? Are we going to call-- Do we have an HTTP.get? Do we have that? I don't think we included an HTTP.get or did we? No, we didn't. The reason I can grab these things-- Hey, Joe and Brian, Raymond W. is wondering if search shouldn't really be considered memoizable, should it? It's all pure. If it gets a different input next time, if search is a different element the next time, it's all just going to be like, For this input, I'll give you that output. Since you're going to an external URL to get your output, that body can change, right? But we're not, though. We're just getting a Future of a possible thing that will happen. When you call search, it hasn't done anything. It' just super lazy. It's like, Here's a thing. It's the thing I always give you. And when you call that thing, you get different results. But this function always gives you the same little thing. It's like, here's some of the fuse when you light it. It's all yours. It's always the same, though. But you'll just get a Future of this particular API call. And so as you check different things in there, that pure stream that would be input _____ returns a new function because it's new input will return a new Future of the different URL. So, we have HTTP.get. That's all we have to do. It's compose-- And that's where you're saying there is some cache-type stuff or are you saying the same thing twice, it caches the result? Memoization, it's just like you can trade speed for memory and get---if I type in the same search results, I should get the same exact Future and the same-- This thing isn't much to memoize. It doesn't have to do very much, but if it's some kind of numerical function or something, that would make much more sense. It's returning the same Future every time. So, I actually changed it from the URL because if all it took was the URL, it would be identical with HTTP.get. So, I had to first---it's going to take a term. It runs it through termUrl, so cool. I wonder what'd happen if we just log that, so we say-- Well, we'd have a Future but nothing would happen. We'd just see a Future there. We could do a search stream. Why don't we fork and log the results. That onValue(log), now onValue's going to get a Future, right? Oh no, you're going to change the urlStream to search. Or we could---should we make a whole new name stream and just map search over urlStream? Oh, it looks like search take a termUrl. Yeah, you're right. Wait, I think it should be a urlStream, right? Wait a minute. If we're already getting URLs, I already used---hang on. Shouldn't it be urlStream at the end? Let's just change termUrl to urlStream. Oh, let's call it searchStream then. I don't even know what this was for. Now, we have to map HTTP.get over that stream. Up in your search stream where you compose, it should be get. That urlStreams a stream so we've got the map(HTTP.get), and we should be logging Futures right now, but we might as well go one step further and just do fork(log, log)). Let's see if it's not blowing up. What are we getting undefined? Oh, it's get capital JSON after get. So, that onValue where you log it, we need to fork---or we still have to call onValue because that'll subscribe to the EventStream that comes out of the IO. And this is getting a little crazy. This is where you're like, I have to deconstruct all these pure types to get the values out. And that's in my opinion worth it, but it's definitely back to that discipline thing. For all the pure stuff in your app being so nice and easy to work with and you don't have to worry about callbacks and everything composes, whatever. Down at the bottom, you're like, Oh, now I have to get it out. And that's fine. It's one line. Because it always ends up as one line. There's only going to be one thing. I don't think onValue returns anything, so you can't chain for it. And fork takes a one argument because the other log is already---I shouldn't have done it that way. What's the failure? I just faked log as a failure. I should've taken two and a Future. Where does this come from? Does it come from one of your point free-- It's right there on 19. I would change 19 to error, success, and Future, and then just fork--- Future.fork(error, success). It's so much nicer. Change it to what? E-r-r s-u-c-c. Error first? Oh, because of left and right. And then just inside, it's error then success. And you need a common between succ and the future. So, now (log, log). Let's see some results. People are asking if it's---if there's an easier way to test along the way. There is. It's called a strong type system. I have to tell you so these are results of a YouTube call which is really cool. But typically your app above is all returning pure stuff, so it makes it dead simple. Everything takes an input and returns an output. If you've ever had to test a pure function, you're like, Whatever, this is so much easier than mocking a thousand things. So, if you decide to test all the pure stuff while you're going along, it's going to be so much easier than setting up objects all to be in the right state, now run your test. And that bottom thing is like your kickoff line that actually opens up values and does stuff. This is crazy land, but each of these could be tested individually and easily and reproducibly. If you're saying, Oh, my search stream just---all these things work. I just have to get the stuff out of the types. So, we're getting fun stuff here.
Demo Part 4
So, we could do-- another thing that's given when we call these results. We could get the entries, what they call entries out of it. Let's just say getEntries. We really will want the URLs of each video as we're moving along. So, feed, entry. And for each of these, we're going to pull out Id, $t. Now, let's do it. One other---somebody said this makes them want to learn Haskell. I'm like, Yeah! You can't just go to work tomorrow and be like, Let's use Haskell for our next project. They'll be like, Okay, well, might as well go bankrupt right now because how many barriers to learning Haskell are there? But this is cool because you can mix this with your normal day-to-day stuff and get really good and learn it. And when we go to Haskell, we're running basically Haskell code right now. Haskell's like the spirit of Dot. Get of my high horse and write some code. Something that maps over---I guess we ought to get the entries and pluck some stuff out. What are we going to call this thing? VideoUrls. And it takes JSON, and it goes to a bunch of URLs. We'll see if this is more than we can chew for this one. So, we want to get compose(get) in reverse order ('$t'). Probably this one we'll have to map again because entries and arrays are just functors as we know. I'm going to write it in reverse because I'm thinking that I should just be using type. So, in reverse, we have to get('feed'), and then when we have feed, we're going to get('entry') I think. You need an _.get. We've got that, and we're going to map over this because it's an array. And it's going to be get---it's going to be another compose. That's kind of nasty. Just write a function there, just be like, What are we mapping? It's going to be called getEntryUrl. I thought we did this before we did entryToLi right there. We do URLs here and then turned the URLs into the list. Yeah, let's do that way. Oh, no, because before we hit that problem where the entry had all this data on it, and we just wanted to use a normal function right there to get it all out. We don't want to lose stuff. Oh, no, we can do that with the URL. I just want the URL ultimately. But you want the title in the li. Oh, you're right. We want the title too. So, a little secret knowledge. We've built this before as a test to make sure we weren't going to be embarrassed. And we're still embarrassed. It's still embarrassing. Stupid YouTube video. What kind of response is that? That's the most ridiculous response I've ever seen. I want to know who's responsible for $t. So, we'll get an entry, but this entry will be _____ to just those things in it, our URL and a name. What we did last time is just turned it directly into our HTML because it was really convenient to make it a DOM element with the title as the--. So, rather than take this thing, sanitize it so that it only has what we want and then take that sanitized thing to make our HTML, we could just go straight from the whole thing to HTML pouring out the stuff we need. We could probably---after we get these results, this is probably the last step before we should probably start wrapping it up, I think. Unless we want to go for the _____, but it's already 5. Or you know what we could do is finish this last thought, jump to the last commit, start examining things along the way, and see it in action because you've got to see it happening. So, we want to do entrytoLi, right? Somebody is very unsatisfied with what we're doing because they didn't get to learn something. We'll have an entry, and an entry will be one of these things, which add and Id in the title. So, let's make---what are you thinking? I probably have to write an actual function. Yeah, just write a function that just makes a jQuery element. We did this before, and it was just too much trouble. It took the entry-- We want to do the li with the---we used the jQuery constructor because we had problems with the data-YouTube thing. And that's a much better idea, a very much better idea. We're going to make---so, we actually want the HTML to be---wait a second, so we make this, right? And then add stuff to it. Or you could just give it an object for a second argument. You could do text and data-youtubeid: elt.id.$t, okay, and elt., and text goes to elt.title.$t. So much for our composable stuff. So, now it's right here, our videoLis, wait. What was this before? So, we have---what's coming through right now? It's a stream of results, of actually an API result. We want to find the right spot and after, and then make HTML after that. They're worried about us making an li here. It's just easier. We don't have---we're not dealing with handlebars---we're not going to bring handlebars in for one li in this whole project or data bindings or something. Another question I was bringing up before was, Is this impure we're making this thing? But in a way, it's just kind of hanging out in memory the way a new whatever, object would be. It's not attached to anything. It hasn't changed anything. It's just hanging out waiting to be applied. And it's not HTML anymore. I think you need strings around that first property, that data-youtubeid. Oh, because of the dash. I want to get to another visible thing. Do you want to just log the lis? Instead of logging the lis that come out, why don't we just call setHTML with them, and then we'll just get a list of the YouTube sites. So, instead---log is the error, so the right side of fork is going to be our success function, which just basically setHTML with our results in it. Do I have to run IO here? No, we didn't put that in IO mainly because it's the end of the line. There's no need to be pure at this point. You're going through all these types. Everything above, our pullout is pure, and then we finally get our Future back. Why would we put it in an IO and just take it right back out? That's silly. If we want to put it in an IO, we certainly can, and then the next person can take our app and extend it. But we don't plan on doing that at this point, we're just running it out. So, we need a new stream at this point. A stream of lis. So, you're getting the JSON from the search stream. So, we want to map over the searchStream. EntryToLi, and you want to map over the searchStream, or compose the searchStream. Switch our searchStream to liStream and we're good. Let's see what happens. I don't have high hopes. Now wait--(typing) It's saying I can't call $t of undefined, so this Id, elt.id must be undefined. What are we calling-- oh, this is funny just funny. So, this is a funny situation. So, what's happening is search is returning us a Future that we're mapping over. But searchStream is returning us it is an EventStream, and then it's going to return us a Future, and that Future returns us an array. That's three functors deep. So, if we do map(map(map)), let's see what happens. There's even more actually because it's returning us the thing that has feedEntry, whatever, we haven't even pulled that out. It's like deep in there. So, why don't you do map---let's do resultToLi, and then we'll do entryToLi. Or resultToHtml. Switch entryToLi and just make it resultToHtml. So, make this resultToHtml. We don't have a resultToHtml, do we? Yeah, we'll just make that. We're just going to basically be like, Okay, we have a Future here. We're going to map over the Future and-- Do you know what I feel like doing is at this point going to the end and just showing them the div and just commenting briefly on what happened in each div because it's seeing the flavor, and the rest is just us debugging. You get to see the pain of us trying to open up types. It's the same pain that's trying to compose callbacks and ___backs. Having to debug it, nothing---normally you type and someone says, That's wrong. You can't do that, don't even try running that. But at this point-- So, from blank slate to create searchEventStream, I log in. That's kind of one of the first things we did. We have to get IO stuff like we did before. We made our listen like we did before. This is easy. We already did this stuff. Oh, that's in a different file. Where's a cool one? Transform EventStream of keyups to that of values. We did that too. Can you just zoom to the end and just watch it run and look at it for a second. Okay, fine, fine. We probably should walk through the final code. Did we ever commit the last change we made? I thought so. Render video search results. Click brings up video, this should be it. Let's look at it and debug it, and this'll be our last bit. I want to see what kind we thought had changed. Let's open it up. This is going to look different because every time it's slightly different. The only thing we did extra pretty much was that we changed forks, so we just gave it success callback down there, which would probably be better to take it to the caller. We should have log baked into the helper. I want to try it on canary. I remember one time that made a difference. It was like a security thing I was trying to do to be helpful, and it wasn't. Or I guess it was helpful but not to us. Please, funny animal video, please work. Oh, it was down here. It's just too small, it wrapped. And the final payoff. This is what functional programming gives you. Yeah!
Final Thoughts
We had two streams we ended up making--our searchInputStream and our clickStream. And our clickStream, how would we read this? What happens? There is a cool part of it, actually, because what we did is we put it on document. We bound to document so that it was the Maybe thing. Do you remember that, Brian? Oh, yeah, so in that YouTube Id, we tried to get the YouTube Id--any document click. So, we just take any document click and put it in a Maybe, and then if it doesn't have a YouTube Id on it, if it's not an element with a YouTube Id, we just don't put it on the click. So, like I'm clicking around on the page but not on an li, it gets the click, it goes through the stream, it eventually hits this data-youtubeid thing. It tries, but this could return not a Maybe, like a bad Maybe, a Maybe you don't map over because the thing you clicked on didn't have a data-youtubeid. Had you clicked on the list, though, it would get picked up. And it would get what it needs. So, the click could be anything. I don't know how sound it is, but it's fun. It's interesting to see that it can take anything, and it knows what do to because it's in the right functors. The bottom line there, I think we probably refactored a little bit before we pushed the main thing, because that's just kind of like, Why are we map composing this stuff? Like Player.creates pure. Why doesn't that go up there? I would probably put the Player.create inside some kind of---it's just returning HTML. So we're saying onValue, get the YouTube Id, but why not just get the player right there and then we'll set the HTML to the player. I think the reason it doesn't come up in regular chrome is because of an iFrame restriction or some kind of like a YouTube--. I think that's---we should push this through refactor for a second, and then push it as an example of something you could do where your whole app is pure, easily tested, memoizable, portable, whatever. So, we'll put all this stuff---the groups in YouTube and all the libraries you saw, the slides, all will revert any changes that happened to JS Bin and all this stuff will be available for you to try again. And if/when you have questions about all this craziness, just tweet at us or whatever, and we'd be happy to help. I said I didn't have time to get to applicative and monoids. Those are so much easier than functors and monads. So, if you look at the slides, and you guys have questions, just email me, or we can try to have some kind of thread follow up. Maybe we can get an email list of just like, Here's the rest of the slides. Ask me questions on the list or something. I don't know if there's a way to get that. But we'll just figure it out or we'll set it up and you can sign it. We have a form. We just don't use it. So, I can start a newsletter or whatever and sign everyone to it. So, in case you were dying to know what applicatives were, you could talk to me about it. And the other part of it is there's a lens library that---it's mind blowing if you know how functors work. Otherwise, it's just neat. So, if you guys go check out lenses and kind of understand, it's built on F-map. That's all I wanted to say.
Course author
Brian Lonsdorf
Brian Lonsdorf is a regular speaker at conferences and helps organize/host FP events around the Bay Area. He is co-organizer of the SF JavaScript Meetup.
Course info
LevelAdvanced
Rating
(219)
My rating
Duration6h 3m
Released9 Apr 2015
Share course