What up Stranded in Halligan? So, I haven’t posted on this blog years, but today I figured I might do something a little different from C++. This tutorial is on the notorious concept of monads that makes Haskell so hard to learn. I’m still pretty new to monads myself, but once I grasped the concept, I realized something. Monads may look like a concept that you need to work hard at to understand, but really, monads are something you can understand even if you are more slothy. And this tutorial demonstrates that if you know about sloths, it’s easy to get a sloth-clawed grasp of the concept of monads.
Now, if you came here looking for a monad tutorial, chances are you are learning them to use in Haskell or some other functional programming language, so I will be putting snippets of Haskell code in the tutorial, but if you don’t know Haskell, no worries. The Haskell code I am writing is very intuitive, and I wrote the code to look like English.
With that being said, let the slothing begin!
Sloths have a lot of fur, and inside that fur is an ecosystem of its own. Sloths can have organisms such as algae or sloth moths in their fur.
data OrganismInFur = Algae | SlothMoths
But if a sloth just took a bath, then the algae won’t grow on the sloth because the algae think hibiscus-scented shampoo smells gross. And since there’s no algae on the sloth, no sloth moths will land in the sloth’s fur. So a sloth can either be a clean sloth or a sloth with something on its fur.
data Sloth something = CleanSloth | SlothWith something
If sloth moths see a sloth full of algae, then they fly in and eat the algae. If sloth moths leave the sloth to go find another sloth that has algae to chow down on, then the algae grows back. This can be described with the `evicts` function
evicts :: OrganismInFur -> OrganismInFur -> OrganismInFur
Algae `evicts` SlothMoths = Algae
SlothMoths `evicts` Algae = SlothMoths
So algae can evict sloth moths and sloth moths can evict algae. But the sloth moths and algae evicting each other only tells us who is leaving the fur and who is moving in. So you can say that sloth moths evict algae, but we never mentioned that they are on a sloth’s fur, so we just have a bunch of sloth moths flying around the rainforest. You can’t say that the sloth moths are evicting a sloth with algae because then the sloth moths are evicting the sloth from itself. So SlothMoths `evicts` SlothWith Algae causes a type error because SlothWith Algae has a type “Sloth OrganismInFur” rather than just “OrganismInFur”
What we want to talk about is sloth moths that find a sloth with algae, evict the algae, and go into the sloth’s fur, and the end result is that there is a sloth with sloth moths. Either that, or algae find a clean sloth, get grossed out by the smell of shampoo, and then leave with the sloth still clean. In both cases, we start with a sloth, something goes on in the ecosystem of its fur, and we end with a sloth. To be able to talk about this, this is where monads come in.
If a flock of sloth moths goes into the rainforest, finds a sloth, and climbs into the sloth’s fur, then what returns from a long day of slowly climbing trees is a sloth with sloth moths in its fur, which is described with the function “return“.
return something = SlothWith something
ex. return SlothMoths = SlothWith SlothMoths
If the sloth moths go into the rainforest, find a sloth with algae in its fur, end then evict them from the fur, then now we have a sloth with sloth moths in its fur. This is described with the function “nowWeHave”, which takes in two organisms and gives us a sloth with the first organism in its fur.
nowWeHave :: OrganismInFur -> OrganismInFur -> SlothWith OrganismInFur
nowWeHave organism1 organism2 = return (organism1 `evicts` organism2)
ex. nowWeHave SlothMoths Algae = return SlothMoths `evicts` Algae
= return SlothMoths
= SlothWith SlothMoths
So when the sloth moths find the sloth, evict the algae, and enter the sloth’s fur, we start with a sloth and we end with a sloth. This relation is described with the bind function, with the cool but intimidating operator >>=. If this this function gets to be a tough branch to climb, just reach your claws over to the example and follow along and it will be smooth slothing from there.
SlothWith something >>= function = function something
ex. SlothWith Algae >>= nowWeHave SlothMoths =
nowWeHave SlothMoths Algae
= return (SlothMoths `evicts` Algae)
= return SlothMoths
= SlothWith SlothMoths
So, in the first line of the example, we see a SlothWith Algae on the left side of the >>=, which is the sloth we started with. What we have on the right is nowWeHave SlothMoths, which is the nowWeHave function that took in one argument and needs one more argument.
In the second line, nowWeHave takes in the Algae from the sloth, and the function nowWeHave is applied. The end result, nowWeHave SlothMoths Algae gives us a SlothWith SlothMoths, so the end result is a sloth with sloth moths in its fur.
But if the sloth moths find a clean sloth, they don’t evict any algae and they have no reason to get into the sloth’s fur because there’s no algae to eat, so they leave the sloth alone and what we have is a clean sloth. Once again, we start with a sloth and we end with a sloth.
CleanSloth >>= function = CleanSloth
ex. CleanSloth >>= nowWeHave SlothMoths = CleanSloth
So on the left of the >>= we have a clean sloth and on the right we have the function nowWeHave SlothMoths awaiting one more argument. But instead of taking in another argument, the function is simply ignored and we get back a clean sloth. So the sloth threw away that function like it throws away the pile of leaves it was eating before it saw some hibiscus flowers to eat.
Now, here’s the interesting thing about this. You know how when we call >>= we start with a sloth and we end with a sloth? Well, what that means is that the sloth we get back can be used in another >>= and we will still end with a sloth. For example,
SlothWith Algae >>= nowWeHave SlothMoths >>= nowWeHave Algae
= (SlothWith Algae >>= nowWeHave SlothMoths) >>= nowWeHave Algae
= SlothWith SlothMoths >>= nowWeHave Algae
= SlothWith Algae
So we can chain together >>=’s to make a sequence of events take place. And a sequence of events sounds an awful lot like the way we do programming in languages like C, Java, and Python. And that makes monads incredibly useful. If you are a Haskell programmer, your first encounter with monads was probably when you were learning how to do more than one IO statement in a program in a purely functional language, and ultimately IO statements are themselves a kind of monad. So monads make it possible to do a lot of real-world applications of Haskell, and they make it possible for you to be the classy math nerd at parties (note that the >>= operator turned sideways looks kinda like a funky wine glass. That’s because monads are that classy)!
Now, for one more example, this doesn’t show much about the concept of monads but more the polymorphism of the >>= operator. Though really I am just adding this example because baby sloths are so adorable!
So, the data type for a Sloth was Sloth a, so the a means that the sloth can have something of any data type on its fur. So a sloth can have a baby sloth on its fur, as we are about to see. Also, the type signature for the monadic >>= function is
>>= :: m a -> (a -> m b) -> m b
In other words the function that a monad can take in can replace what was originally on the monad with something of a different type, so while the nowWeHave function replaces an OrganismInFur with another OrganismInFur, another function, babySloth, replaces an OrganismInFur with a Sloth.
babySloth :: Sloth a -> b -> Sloth (Sloth a)
babySloth CleanSloth _ = return CleanSloth
babySloth (SlothWith something) _ = return (SlothWith something)
So, here’s a sequence of >>=’s to demonstrate that
SlothWith Algae >>= nowWeHave SlothMoths >>= nowWeHave Algae >>=
babySloth (SlothWith Algae)
So, what do we get here? Well, we know that from the two nowWeHave’s, the result is that we start with a mother sloth with algae, she sloths a few feet up the tree and some sloth moths eat the algae, then she goes up the tree to go eat some leaves, and the sloth moths leave her fur so new algae grow, and then she goes over to sloth preschool to pick up her baby after a long day of learning how to sloth around the forest in search of tasty leaves and hibiscus flowers. So now we have:
SlothWith Algae >>= babySloth (SlothWith Algae)
Which gives us:
babySloth (SlothWith Algae) Algae
Which gives us:
return (SlothWith Algae)
Which ultimately gives us the result
SlothWith (SlothWith Algae)
Which is a mother sloth with a baby sloth on her back.
If you understand all that, congratulations, you just took your first step into the world of monads! To celebrate, go brew yourself a cup of hibiscus tea! We’ve just scratched the surface of monads with our sloth claws, but from my experience, once you understand the material in this tutorial, the rest makes a lot of sense, so sloth on down to all the other great monad tutorials online.
I’d like to thank Learn You A Haskell for being the tutorial that ultimately helped me figure out monads and the Aviarios Del Caribe Sloth Sanctuary in Costa Rica for showing the world how adorable baby sloths are and ultimately being the inspiration for this tutorial!