The Dongola Times

(Anachronistic) Dispatches from the Kingdom of Makuria.
27th of February, 2012

The Day Python Embarrassed Imperative Programming

This picture is like finding out that the Pope is not a Roman Catholic.
Python Monads Lesson
What you see there—click it for a much larger version[1]—is the result of a very innocent question from a Python programmer to me, after he found out that I am a Haskell programmer. I will give you the lesson that I gave, of monads in Python, using that whiteboard.

The Python programmer said he had downloaded Haskell, and was experimenting with it. “But what is this monads stuff?” Never. Never ever ask that. One negative effect of asking that question is the horror of finding out that you use monads more than Haskell programmers; but because they are everywhere in your life, you have developed a blind spot to them. (Hint: do you use nullable objects?) In fact, as I showed him, every single object in Python is an instance of [the Haskell type class] Monad. So, to prove this ridiculous statement, I told him (upper-right corner): “Monads are conditional function calls.” So, every time you see a function being called inside an if statement, you are looking down the howling void of a monad.
On the whiteboard, below that word “MONAD”, you can see the Python function, pymon, whose definition is:

def pymon(f, v = None):
  if v:
    return f(v)

And with that in hand, I proceeded to explain.

In Haskell, one example of a monad is the Maybe anythingElse type. It has two possible values: Nothing and Just anythingElse. In Python, every object is an example of a monad. It has two possible values: None and anything_else. In both these cases, anything_else can be any value. Right. So, every time you ever execute a function based on which of the two values is present, you have implemented the heart of Monad. So, what makes it cool in Haskell is that this removes about six hundred and forty nine thousand two hundred and eighty-eight if statements from your code. Much of your code is feebly re-implementing what pymon is doing up there. If you could shove pymon at every object, you would find that your code focuses on getting the job done, not baby-sitting if statements.

On the whiteboard, I proceeded to implement pymon in Haskell—as hamon—which became what you see here:

hamon f v = v >>= f

So, you see that what increased the cyclomatic complexity of pymon has been achieved in Haskell without any increase in cyclomatic complexity! That is a win, because if you can get away with conditional application of functions without littering your page with if, if, if, if, if, you can implement large logic without fear, and in a terser, more-compact, well-manicured way. The trick is that >>= is the heart of the Monad, and we already implemented it up there in Python (as I will show).

I proceeded to say (on the whiteboard) that “v >>= f” is this in a pseudo-code that looks like Python:

if v has something in it:
  return f applied to what is in v

Which, the perceptive reader will notice, is exactly what pymon says.

So I implemented hamon2, which specialises hamon to deal with one particular monad. (This is to basically show what >>= is doing.)

hamon2 f (Just x) = Just (f x)
hamon2 f Nothing  = Nothing

This obeys a rule about monads—followed in both Python and Haskell, whether we realise it or not (Python), and whether we like it or not (Haskell)—which is that a function that deals with a monad at all shall always return a monad. That is what is written to the middle-right of the whiteboard: “>>= only ever returns monads”. But the best thing here, of course, is that there is a simpler way, shown before, to implement hamon2. And you can even make it more-general than hamon2, such that it does not deal with just Maybe a, but with any Monad. In Python, this is indeed the case, because every object is a monad already!

hamon3 = (>>=)

In English, the above line says: hamon3 is the longer name of >>=.
But hamon3 is equivalent to hamon2.
And hamon2 is equivalent to hamon.
And hamon is equivalent to pymon.
And pymon is equivalent to >>=.
And >>= is the heart of the Monad.
And so … you have been using monads in Python, all along sneering at the “smug Haskell douchebags who brag about the size of their gonads or something”. You are one of them. As my favourite bald-headed short man once said, “You who judge, do you not do the same things?”

So, both Haskell and Python use monads extensively; and Python does it so much more than Haskell. What, then, shall we say?
What makes Haskell’s take better is that it is simply too reusable. Do you know how it feels to write a complete JSON parser without having a single if statement to handle failure (bad syntax), and yet handling failure anyway? I do. Have you ever checked a parser’s 200 lines of code and seen only three if statements whereas every single function in the code handles a case of failure? I have. This is nothing spectacular, until you have to live without it—without pattern matching and such great abstraction as is provided by things like the Monad. The trick of that JSON parser is that no function even deals with any particular Monad—it just deals with any monad: “anything of which we can say ‘if v has something in it, return f applied to what is in v’,” which happens to be nearly anything more-complex than the byte.

Lists? They are monads. File handles? Monads. Database query results? Timer countdowns? Fuel tank status? Monads. Even simple, direct, applicable understanding of monads is a Monad: if you grok, apply. And yes, even parser results are monads.
Because it is simple, you can live without it. But because it is simple, it shows up everywhere; so it is good to have a solid and ready toolkit to save you from a swarm of the case. Learn you a monad for great good. It is the little things, brother. Do not go at the monads so that you can learn some clever move; it is a simple, mundane thing that Java programmers implement every six lines they write. Just go at the monads to stop being that bored/boring. Deal with real complexity; not

if(x == null) return null;
//  Proceed and use x

As a parting coup, I gave the Python programmer a link to the JSON parser I built in Haskell, some years ago. My company uses Haskell, because we do not have much manpower. We do not get paid enough, so we take many contracts. We cannot afford QA teams, so we use a sadistic compiler. We cannot dedicate too much time to any particular project, so we prefer to learn dense idioms (which takes time upfront, and saves time later), rather than learn sparse idioms that take more time to implement every time and debug individually. We do not keep a project in live mode after we have handed it over, due to little manpower, so any bugs that we have to cure at our own cost must be caught in development. Now, if you have few programmers, then they should be very efficient, and the best place to optimise is the tools. To make a sufficiently-fast programmer faster—like a sprinter running on a conveyor belt—make him stop dealing with gratuitously-explicit if statements for a start (this is what makes threads difficult, for example: too many ifs). Enter monads.

It was embarassing to find that the language that we thought had shielded us from the assaults of the impractical ivoritoweritis of Haskell was always an accomplice in making us expert at implementing these self-same things at which we laughed. I have known betrayal—I have seen it, I have stood at a whiteboard and denounced it for what it was—and I took a picture of it.

[1] I use a Nokia N8. 12 megapixel camera. Terrifyingly-huge images. I love it.