Check out a free preview of the full Hardcore Functional Programming in JavaScript course:
The "IO" Lesson is part of the full, Hardcore Functional Programming in JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

IO is a functor that is considered a lazy computation "builder". It's typically used to contain side effects. So rather than contain a String or Array, it will contain a function. A map appends the function to a list of items to run.

Get Unlimited Access Now

Transcript from the "IO" Lesson

[00:00:00]
>> [MUSIC]

[00:00:05]
>> Brian Lonsdorf: IO, this one's pretty crazy. Are you guys ready for pure IO? [LAUGH] This is a functor, but instead of a number or a string or a list inside it, it's gonna put a function inside it. That's pretty crazy, all right [LAUGH]. So it's like a computation builder.

[00:00:27] Every time you map over in IO, it's gonna basically secretly compose inside. So we'll see it in action. This is gonna 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 gonna be a little bit different in that regard.

[00:00:52] Okay but yeah you can use it to obtain side effects and eventually it just returns your 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. So yeah. Let's look at this, so if I have an IO of a function that gets the e-mail's value.

[00:01:18] I probably should've used document create selector. Anyway, I'm using jQuery here. So shout out to map rules. [LAUGH] So, the IO is taking a function that returns us whatever is in the email field's value. Now, if I wanna say like welcome, whatever your name is, I can cat welcome to.

[00:01:40] 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.

[00:01:59] So we'll say like welcomesteve@foodie.net. [LAUGH] I don't know, so it builds up a really lazy computation at the end. You call runIO with it. See another example. That's runIO. [LAUGH] 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.

[00:02:28] So let's see here. If I wanna get the preferences out of a store, let's look at this part right up here. If I get the preferences out a store, let's pretend this thing returns me an IO, like 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.

[00:02:51] There's data somewhere, it just showed up. It should be known that this thing is coming from the outside world into my app. And I wanna flag that by the Store.get("preferences") will return me IO preferences. It's not just gonna return me preferences right up off the bat. So nothing will happen when I run my app which is really nice for testability, extendibility, I can keep building upon my app without it ever running.

[00:03:19] So if I map, get the background color or parse those preferences because they assume presumably adjacent string. And we get the background color. Now when I run my app, 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.

[00:03:40] There's an intuition here that you can kind of go with. Go with your gut instinct. What's gonna happen, if Store.get returns an IO and then map over to get the background color, who cares what's going on? Like it's just gonna gave me the JSON and I'll parse it, whatever.

[00:03:53] But at the end, we gotta do a special runIO to kick it off.
>> Speaker 2: So if I'm assuming, you can embed IOs within other IOs?
>> Brian Lonsdorf: Yeah.
>> Speaker 2: And then if you do runIO on the one that encloses other IOs. Does it recursively run?
>> Brian Lonsdorf: It does not-

[00:04:14]
>> Speaker 2: Every time you go back an IO then you have to-
>> Brian Lonsdorf: Take next step, next step.
>> Speaker 2: Yeah, if it's a IO, then run it.
>> Brian Lonsdorf: That is correct. But monad solved that. So functors are going to do that to you. As you use functors you 're gonna be like uh-oh, I have a maybe, and a maybe, and a maybe, or an IO, and an IO, and an IO.

[00:04:31] And monads allow you to join those together, so just have one. And if you end up with that, run, stop, run, stop.
>> Speaker 2: So you can combine a bunch of IO to roll them up. And when you do runIO, it runs them all?
>> Brian Lonsdorf: It's basically, we'll get to it with monads.

[00:04:48] But the answer to your question is, if more than one IO I can make it one IO. And then I just run one IO.
>> Speaker 2: So it's almost like the promise interface but you can embed promises within promises.
>> Brian Lonsdorf: That's, ooh, gosh, I just slapped that. Yeah that's exactly what this is, it's like a promise.

[00:05:06] And you put your functions in your promises. You say then do this, then do that, then do that.
>> Speaker 2: But promises run-
>> Brian Lonsdorf: They'll run immediately.
>> Speaker 2: Right, immediately, [CROSSTALK] this is like lazy loading promises.
>> Brian Lonsdorf: Beautiful, that's exactly what it is. I'm glad that you brought that up.

[00:05:22] Then we're gonna talk about, actually, I think we're gonna talk about it right now or right after this.
>> Speaker 3: Brian, before leave that side, there was a question. Why don't we need to map GET over JSON.parse?
>> Brian Lonsdorf: Because we map get background color over those preferences. So that map has opened up the IO and passed the preferences into getBgColor.

[00:05:46] getBigColor just gets the preferences right off the bat. So, it's like a thousand maybe, no you map over it and the value inside the maybe goes in 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 like.

[00:06:04] Return me an IO and i'll just eventually map over the value it returns. So does that clear that up? You open up the IO and boom, there's the preferences waiting for you. And you just give that to getBigColor, which we'll parse. Preferences, if you get it from a store, I was just kinda showing that it's probably gonna be JSON string.

[00:06:24] So we'll parse that JSON string. Plus there's an example that's this exact thing. Don't look at it anymore. All right. [LAUGH] I wanted to, last thing before we do a couple more exercises here and that is. We are running low on time. All right. Last thing here is that email_io has an IO with a function inside it.

[00:06:48] 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. Like 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.

[00:07:07] The reason we have that bottom helper here, is be able to be able to pass arguments to the function inside the IO. Because if I didn't call toIO on the end, it'd be very hard for me to get the selector in there. Anyway long story short, we're gonna call toIO at the end of everything.

[00:07:23] Instead of wrapping it in IO, just as a helper function that will help us get through things without having problems with our units.