Hardcore Functional Architecture Patterns in JavaScript

Reconstructing with Monad Transformers

Brian Lonsdorf

Brian Lonsdorf

Hardcore Functional Architecture Patterns in JavaScript

Check out a free preview of the full Hardcore Functional Architecture Patterns in JavaScript course

The "Reconstructing with Monad Transformers" Lesson is part of the full, Hardcore Functional Architecture Patterns in JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

Brian introduces monad transformer based libraries, and explains that each transformer is useful for a specific task, and reconstructing code with monad transformers requires understanding which transformer to use at the right place.


Transcript from the "Reconstructing with Monad Transformers" Lesson

>> This is where things get a little bit intense. So I can say I have a function transformer of, let's say, a task, all right. And we'll call this a function task. Then I could say I have an either transformer, well let's do, yeah, let's do an either transformer.

Either transformer of function task, [LAUGH] and we'll call that my app. All right, what's happening here? Well, I have a transformer stack inside a transformer stack inside a transformer stack inside a transformer stack. So my app type really is, probably should be expressed without all these kind of transformers, as I haven't either holding a function holding a task, and I'd like to just continue that shape.

Let's say my app just continually might have an error, and if it does we just wanna stop. But if it doesn't keep going and then it has a function inside that which holds a task, cuz all these functions just do side effecty things, so we're gonna side circuit early.

We have a bunch of functions and each of them is running a task. Cuz that's a type of my app right there, right. And this is kind of what you're gonna see with these Monad transformer based libraries and functional programming is that you get these kind of stacks of transformers.

And you have to kind of figure out how to get your stuff in the right spot and lift. So let's let's see how this works. Actually haven't tried either T, [LAUGH] so let's go ahead and see what's happening here. I might have to swap that one out. Console that log, res, see if we're just level on the original.

Yeah, it's not working, all right, let's see what happens. It's just not the right path, all righty. Okay, so that seems happy. so I have app of two, map x x plus one. Okay, so this should be three, I have an app of three. All right, but what is an app?

Remember, it's an either holding a function holding a task. So let's try to get at this value. So we'll start with rez.fold. I'll do just an error if we get one. Otherwise we get a function out, all right. Let's run that function with some perceived environment. Myenv, true.

All right, which gives me a task. [LAUGH] Let's give ourself some room here. Right, so we've got our function, I'm sorry, we have to run it, and then I get a task out of that. So I can fork that task, I'll console.error, and then we'll console.log. Okay, so we have this thing here that it can map over all I want, and when I tried to run it and see what happens.

Do we get a value? We get three out, terrific. So that's just working with app. And the challenges come from when we start to return eithers or functions or tasks within these things if we chain. So I put three cases in here of chain. Map is awesome, right, I just do stuff and I don't have to worry about anything, it just bottles it right back up for me.

As soon as I start using chain, I have to figure out what is my function gonna return. We're not usually writing this, right, we're usually saying, chain do DB thing, right. That might return a task, for instance. Now I have to figure out how to get my task inside a function, inside a task, either have it either inside that task and so on.

You're gonna have to manually put these shapes together. So part of the reason I'm showing you this is because I don't love Monad transformers. Sometimes they're useful and they're around and it's good to know, and if you keep them shallow they can be helpful, as we saw with the other example, but it's not my favorite technique.

So let's just walk through the first thing. So this function returns a task either of two two, and we have to lift into our app. This next function returns an either, which then I have to put inside a task either, I have to lift it into that, and then lift that into app, right.

So the idea is that my shape I'm trying to get out is an app holding a task holding an either. And since this function returns an either, I'll lift it into the task either and lift that into an app. Next we go into this thing here, and I'm returning a task, whoops, a task.

And now I have to map over that task and put it in an either, so I get a task holding an either of four, and then I lift that into the app, and so on. There's different approaches, right, we can map to get inside the type to put another type inside it, and so on, etc.

But this is where things get really tricky. And we just, it's best to stay shallow in my opinion without tons of type help. There's also a thing called liftIO. I'm not gonna even go into it, but that can kind of mechanically push you all the way to the bottom of the stack, but we're not gonna deal with that.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now