Check out a free preview of the full Hardcore Functional Programming in JavaScript, v2 course
The "Refactoring Node IO with Task" Lesson is part of the full, Hardcore Functional Programming in JavaScript, v2 course featured in this preview video. Here's what you'd learn in this lesson:
Brian explains that Node IO, although commonly used, does not bring in any asynchronicity, and demonstrates how to use Task instead which gives a clean control flow.
Transcript from the "Refactoring Node IO with Task" Lesson
[00:00:00]
>> So, we've introduced the type task. And, love when you mix these. All right, so [LAUGH]. I'm just gonna leave it there for the people who, it's gonna bother everyone. So we've got this big mess of a, I'm gonna bump this down one more, and move this over a little bit, okay.
[00:00:28]
So here's what we're dealing with. We have this giant thing, and we're gonna read a file, and then we're gonna write out the contents, and we're gonn replace the 3's with the 6's, and then we're gonna write the new file out, and it's great. We got a success.
[00:00:44]
And unfortunately, I think I might have actually ran this without knowing it. Yeah, I gotta config one, by, all right [LAUGH]. Let's get this out. So, how would we rewrite this in terms of Task? And Task is gonna capture all of our IO, in node in particular, or the web.
[00:01:08]
There's an IO Mona that does almost the exact same thing. Problem is, it doesn't really deal with async, and everything we tend to work with is async. And instead of bringing in IO and Task, we're just gonna use Task, cuz it basically does all the things IO do, just with async.
[00:01:27]
So, let's bring this in and say, let's rewrite this. All right. So, the first thing we gotta do, let's go ahead and make a helper function for this right off the bat. So we gonna say readFile, and that's gonna take some path and some coding, and it's gonna return a new task, Task, reject, resolve.
[00:01:57]
Now, tasks have different implementations in different libraries. I'm bringing in a library that I've made just for this class, and I'll make that available. But, I've been using Folktale, there's been talk of using Sanctuary. There's Flutter, basically a lazy promise. So, let's pass in the path and the encoding.
[00:02:29]
And also, there's a way to automate this, we don't have to, there's from Promise, Task from Promise. So we can just turn it, and we can turn a call back into a Promise, and a Promise into a Task, or we can call back right into a Task. I'm just doing this manually so you can see how it works.
[00:02:45]
So if you have an error, we'll reject with the err. And if we have this, otherwise, we'll have success with the contents. And there you have it. So there's readFile. And, before we go any further, I'm just gonna do the same with writeFile, because it's just a pain to define these.
[00:03:05]
And, I think I do have the Task from promised on this library, but I'm still gonna rewrite it. Mainly cuz it's the exact same code [LAUGH]. WriteFile, path, contents. WriteFile, path, contents. Great, great, okay. So, how do we use these? Let's leave this on the screen and give ourselves some space, and rewrite this stuff.
[00:03:31]
So const app will be a function that starts by reading the file, readFile, and we gave it the stuff. And now we have a Task with the contents. So let's go ahead and map over that and get the contents. And, don't wanna throw errors or logs that, I think that log in there was left in by accident.
[00:04:01]
Now we wanna replace any 3's with the 6. Just so we all know, the config.json looks like this, it's like port: 3000, that's all, so we wanted to say port: 6000. Okay, so read the file in, and we'll run this part. Then, we wanna write a new file.
[00:04:26]
Cool. So, what we do, a chain. So you have the new contents, and then we'll write the file with the new contents. Gotta give it a path. So config1.json. And finally, we wanna log('success!'), I suppose. So we'll just, success! And actually, logging can be done in a Task to keep it pure, but I don't wanna just do it outside.
[00:04:59]
So our app is totally nice and clean, and pure, and nothing is just gonna happen. If I were to map log right here, console.log, success, that's gonna run during the time of construction, not run during the runtime. I would have to wrap this in another Task right there to make it pure.
[00:05:18]
So what I'm gonna do, so I'm gonna call app[].fork(console.error. And then we'll log, 'success!' down here, it should be console.log. Awesome. All right, do we have a config1? We do. Just got written at 2:56 [LAUGH], and now it's 6,000. Cool, awesome. But let's take a look at what happened here.
[00:05:50]
So we took this beast [LAUGH], and kinda broke it down into, instead of callbacks, and mixing our logic amongst all this stuff, and throwing errors, whatever. We have this nice, clean control flow. And you might be like, well, we have promises, I'll just do that with promises. Well, promises were inspired by functional programming in the first place, that's where they came from.
[00:06:12]
But also, this does it in a lazy way. So, let's say I wanna continue my computation down there, I can do that and nothing will happen. In fact, what if I wanna combine it with another app? And I wanna run those in parallel, or I wanna put them in a tree data structure, and follow them down the tree, and run the right one.
[00:06:32]
I can do a lot of amazing things if I just make it lazy. Still trying to make that case, not sure if I'm succeeding. [LAUGH] But anyhow, that's where we're at.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops