Monads in Ruby, Part 1: Introduction

This article owes a great debt to Monads as Containers and A Schemer’s Introduction to Monads, each of which greatly advanced my understanding of the topic.

Introduction

Monads have been getting a lot of press lately in connection with the Haskell programming language. They’re a very powerful programming tool in functional languages, but all too often they’re described as some sort of black magic rather than the simple construct they are.

It doesn’t help that the concept of monads is often introduced with Haskell’s IO monad. IO is a strange, magical beast which you can’t see inside, and it’s not typical of most monads you’ll encounter. Also you need a good grasp of Haskell to understand it, which is tough because most modern Haskell coding requires an understanding of monads in the first place.

Now, monads are important to Haskell programming, but they aren’t a special feature of Haskell. You can do them in any programming language that support closures and anonymous functions. That means the obvious suspects, like ML and Lisp dialects, but it also includes languages which are more familiar to many…

  • Perl! (via anonymous subs)
  • Ruby! (via blocks)
  • Python! (via its “lambda”, if you can tolerate a little pain)

Wouldn’t it be nice to learn monads in one of those, without having to tackle Haskell at the same time? Monads may not be as efficient or convenient in these languages, but you can certainly do them.

Let’s do them with Ruby. It’s the friendliest of the three.

Prerequisites

Obviously you’re going to have to be familiar with Ruby to follow along; I’m not going to attempt teaching Ruby and monads at the same time. I’d suggest you need Ruby background equivalent to at least chapters 2-6 of Why’s Poignant Guide to Ruby.

You’ll also need to have a handle on the idea of using functions (e.g. blocks, methods, etc.) as objects.

I’ll provide a brief (Ruby-centric) introduction to that, in case you’re only passingly familiar. Otherwise, you may skip ahead.

This is a pretty straightforward thing in Ruby: you can get ahold of a bit of code as an object and then you can use its call method to run that code as often as you like.

Here’s one way to make such an object out of a block:

f = lambda { |what| puts "Hello #{what}!" }

The contents of the block don’t get run right away. Instead, you get an object (of class Proc, in this case) representing the block. Using that, you can call the block from anywhere, like this:

f.call("World") # prints Hello World!

Note that the block will hold onto any local variables from the place it was defined:

def hello_maker(what)
  lambda { puts "Hello #{what}!" }
end
f = hello_maker("Universe")
f.call # prints Hello Universe!

Even though hello_maker has exited, the variable what lives on inside the object we made from the block. This will come in handy later.

One last thing. You can use & to give or receive a block as an object, as the last parameter to a function. For example, you can rewrite this:

def foo(a)
  yield(a)
end

…like this:

def foo(a, &b)
  b.call(a)
end

This second way lets you get the block as an object. You have more options this way, like giving the block to somebody else, rather than simply calling it yourself. This will also come in handy later. You’ll see.

If this stuff is new to you, play with it in irb for a while, then come back. It’s the toolkit you’ll need to work with monads in Ruby— familiarity pays.

With that out of the way…

Meet the Monad

A monad, for our purposes, is made up of three things:

  1. A container type.
  2. the operation wrap: puts a single value in an instance of the container; Haskell calls this (confusingly) return
  3. the operation pass: extracts the values from the container and filters them through a function (we’ll use a block); Haskell calls this “bind” (spelt >>=)

These three ingredients make up a monad. Most monads have some additional operations available, but those vary from monad to monad.

What good are monads? They let you chain pass operations together to make little computational pipelines, with rules of your choosing. They don’t manipulate values themselves — that’s the job of the blocks (functions) you plumb together using the monad.

Hmm. Still a bit abstract, isn’t it?

Let’s make a little monad for real and learn what makes it tick.

Next: Identity, The Littlest Monad