February 28, 2014.
Don’t mind the linkbait title—I know you’re not a dummy. And you’re totally smart enough to understand monads.
There is no shortage of monad tutorials. I’ve even written a couple myself. But these tutorials try to get you from zero to hero at lightning speed. This one will be different. I’m not going to explain categories, T-algebras, functors, or any of that abstract nonsense. I’m also not going to teach you how to use monads effectively, or even try to convince you why they are useful at all.
So what’s the point then? The point is to make you not afraid of the word “monad.” You know the term comes from math. But the way monads are used in functional programming is pretty far removed from their mathematical heritage. So let’s not even bother—your time is precious. This tutorial will be very practical. I just want you to get the big picture and not feel intimidated by them—and you can check out other resources (like my other articles) for the details.
Okay, let’s get started. Oh by the way, I’m not going to use Haskell in this article, which is probably what you think of when you hear about monads. How about something a little more familiar, like Python? Sounds good.
Monads tend to come up a lot in functional programming. I’ve written about functional programming before, but all you need to know for now is that it’s a paradigm in which mutable data is generally discouraged, or even forbidden. Computation generally happens by composing pure functions together. A pure function is one that does nothing but return the result of a computation. It doesn’t print anything, store anything, ask for user input, etc. It just manipulates the data you pass it and returns more data.
You may be more familiar with object-oriented programming. Let me translate monads into OOP-speak. A monad is an object. It has a couple of methods:
class Monad: def __init__(self, x): ... def bind(self, f): ...
xcan be anything.
fis a function that takes something of the same type as
x(presumably from the monad) and returns a new monad. Lastly,
bindalso returns a new monad (often from the result of
f). Literally that’s it. Maybe you don’t know what it’s used for, but at least now you’ve seen the interface.
Typically, a monad acts as some sort of container, and
bindapplies a function to what it contains. Let’s make a monad that stores a single element:
class SimpleMonad(Monad): def __init__(self, x): self.x = x def bind(self, f): return f(self.x)
To make this a little more concrete, this is how you use it:
def double(x): return SimpleMonad(x * 2) def add1(x): return SimpleMonad(x + 1) foo = SimpleMonad(5).bind(double).bind(add1)
Okay, so you can chain functions together with
bind. But why would we do that? Equivalently, we could have just written
add1(double(5)), which is much shorter.
The main idea is that monads don’t have to just call the functions you pass to
bind. They can change the rules. For example, here is a monad that allows you to chain operations without worrying about null pointer errors.
class MaybeMonad(Monad): def __init__(self, x): self.x = x def bind(self, f): if self.x is None: return self else: return f(self.x)
If any intermediate computation in a sequence of operations results in
None, the entire expression becomes
Noneand no further operations are performed (much like how
NaNworks in floating-point arithmetic, if you’re familiar with that). If you spend more time with monads, you’ll find that you can also use them to implement exception handling, mutable state, concurrency, and a bunch of other things.
Remember: functional programming is all about writing programs by composing functions together. The power of monads is that they allow you to change the rules for how functions are composed.
When I was learning about monads, the main things I was confused about were:
- It seems like using monads requires you to write a lot of code, e.g.,
add1(double(5)). Do people really go through all this trouble?
- How do I get the value out of the monad? Can I access it like
- I hear that Haskell doesn’t let you write “impure” functions that have side effects, but I know for a fact that Haskell programs can print to the screen and write to files. What’s the deal? Do monads somehow resolve this?
Well, I know the answers now, so let me share the love:
- Haskell has a special syntax for monads and Python doesn’t—so that’s why they don’t look very nice in Python. It’s actually quite brilliant. Check it out if you have time. Using it feels very natural and it looks a lot like imperative code.
- Some monads provide a “getter” function that allows you to get the value back out. But a monad is not required to have one, and some monads don’t. For many monads, it doesn’t make sense to take a value out of it. A monad is not required to act like a container. See #3.
- You’re right—all functions in Haskell are pure. Haskell has a neat trick for doing I/O. You can think of a Haskell program as a pure computation that returns a tree-like data structure, and this tree represents another program that does impure things.Now, building this tree can be done in a purely functional way—as is required in Haskell. But when you “run” a Haskell program, two things happen: 1) your pure Haskell computation is executed, resulting in this tree-like data structure, 2) the tree-program from step 1 (which can print to the screen and write to files) is also executed.It just so happens that a certain monad, especially when combined with Haskell’s special monad syntax, makes it easy to build this tree. This monad is called
IOin Haskell. The bind operation does not call the function you pass it, as it did in our monad examples above. Rather, it just stores it in the tree. The runtime will call it when interpreting the tree. That’s the high-level idea. In this post, I implement this idea in Python, which will probably make it a little more clear for you.Lastly, the
IOmonad does not have a “getter” function. For example, if you have an
IOaction that asks a user for a string of input, you cannot extract the string from the monad. This is because the
IOaction doesn’t actually have a string to give you—it only represents a computation that returns a string. When you
fto this monad, you’re building a new
IOaction that asks the user for a string and then calls
To summarize, monads let you change what it means to compose functions together. You might want to keep track of some extra information, store the functions in a tree, call them in a different order, call them multiple times, skip some functions entirely (based on the value being passed to them), etc.
You don’t have to use them, but now you don’t have to fear them either!
EDIT: Fixed a typo and added the
MaybeMonadexample at /u/wargotad’s suggestion.
February 27, 2014.
I’m working on an a cappella cover of Say Something, by A Great Big World and Christina Aguilera. Here’s a very early preview:
February 1, 2014.
Over the last few days I built Kitestring, a mobile-friendly web application that checks up on you when you go on unsafe trips.
You tell the application that you’re going on a trip, and then it checks up on you (via SMS) to make sure you haven’t been mugged or assaulted. You reply to the SMS messages (or check in on the website) to confirm your well-being. If you “go dark,” the application alerts a list of emergency contacts that you set up ahead of time.
As usual, the project is open source!
January 7, 2014.
After moving into a new dorm at MIT, I just had to try out my new kitchen. Today’s menu: Cajun jambalaya. Start with a pair of breasts.
Dice some kielbasa to go with it.
Sauté the meat for a bit.
Chop some peppers and onions.
Don’t forget the garlic.
This much seems about right:
Throw in some rice and chicken stock.
At this point, it should look like this:
Toss in a few bay leaves.
Captain Worcestershire and his side kick, Sriracha!
A dash of these things for good luck:
Simmer like there’s no tomorrow!
November 21, 2013.
I have a lot of ideas for programming languages and type systems. Here are a few I’ve been thinking about today:
- A programming language where mutable types are uniqueness types. In other words, you can only mutate an object if you have the only reference to it. Why this restriction? Because all objects are automatically thread-safe! And you get almost all the benefits of immutability, but it’s not nearly as restrictive as a pure functional programming language.
- (This is not really a serious idea.) A programming language with no data type for strings. Why? Far too often, strings are used as an unstructured way to represent structured data: file system paths, SQL queries, XML, HTML, regular expressions, etc. Representing structured data as strings sidesteps the type system, and doing so can lead to injection attacks. This is called “stringly-typed” programming.
- A type system that fits into a lattice, where order is given by the subtyping relation. The concepts of subtyping, inheritance, interfaces, and union types are all unified in this system. Taking the supremum of types
Yis equivalent to the union type
X | Y. Taking the infimum of
Yis equivalent to the type of objects that implement interfaces
Y. Combine this with bounded parametric polymorphism and you’ve got a formidable type system.
- Tracing garbage collection is a cool way to manage memory, but it can cause random pauses at runtime (which may be unacceptable for real-time applications). Reference counting is an intriguing alternative, but it fails to reclaim cyclic data structures. Forget about both for a second. Let’s say you have no access to a system memory heap—and you have no pointers or references either. Instead, you can create “memory heap objects”, and then ask them for memory when you need it. When you ask for some memory, instead of a pointer to it, you get a “key” that indexes the memory heap like a hash table. Now—the memory heap objects are reference-counted, and are never cyclic (even if the structures they allocate are cyclic). When the memory heap is released (automatically, due to reference counting), anything it allocated is released as well. No unpredictable garbage collection stops, no cycles, no memory leaks. Problem solved.
- Using monads to chain asynchronous events, such as futures. Maybe this can be combined with #4 to make a cutting-edge web framework.
- Crazy type systems. Type systems that model quantum behavior. Type systems that model latency in networks. Type systems that model security access levels for web apps. Type systems that model physical units. Type systems that model probability distributions. Type systems that model relational data (as in SQL). Type systems that model proofs. Each of these might make an interesting programming language.