Functors and Monads (containers)

Introduction

In a philosophy class in college, I remember learning the idea that in order to understand something, you can’t simply study it. For example, if you study the heart, you’ll understand the function of the valves, what causes the muscle to relax and contract, and so on, but you’ll have no idea what a heart is and what its purpose is. In order to learn that, you need to study the human body.

I’m going to take the same approach to Monads and Functors (and hopefully later Applicative). I hope this doesn’t turn in another Monads are Burritos. I’ll avoid as much as possible type signatures and such so as not to obscure the main point.

Containers

Two scary concepts in Haskell, Functors and Monads, both deal with containers. These aren’t as limited as containers from a language like Java. There, containers are linked lists, arrays maps, sets; some way of managing collections of stuff. In Haskell, we use containers to represent actions, logging, and- our topic today- things which might exist. I’m speaking about Maybe.

I’ve chosen Maybe since I find it easy to think about. You either have Just a result, or Nothing. To further simplify, we’ll only deal with Maybe Int; a number that might exist.

How can such a thing occur? Let’s say these numbers represent the price for an item, given in cents. We might have a getPrice function which takes an Item as an argument. If a Toothbrush costs $1.25, then the function would return Just 125.

On the other hand, say you don’t have a price for Toothpaste. Then the function would return Nothing. (Think for a moment how you’d deal with this in Java, should a 0 represent unknown? What if you have a buy one-get one free sale? Use -1? You’ll have to remember to check for it everywhere. But I digress.)

Monads

Anyway, let’s now say that you have another function to add tax onto the purchase price. It doesn’t deal with Maybes; it adds 5% to an Int. But our getPrice function returns a Maybe Int. How can we stick these two functions together (ie, compose them)?

This is where we can use the trusty old Monad. Let’s start with do notation:

getPriceWithTax item = do
    price <- getPrice item
    return $ addTax price

That’s a little verbose for something so simple. Let’s drop the do:

getPriceWithTax item = getPrice item >>= return . addTax

Great… except why the return? We have two types at play: Int and Maybe Int. When dealing with one-argument functions, you get four possible function signatures:

  1. Int -> Int
  2. Int -> Maybe Int
  3. Maybe Int -> Int
  4. Maybe Int -> Maybe Int

The third option is unwrapping a contained value; it is not a topic for Monads or Functors, so ignore it for now. (If you care, look up fromJust.) The first option is a regular old function, like addTax. Option 2 is like getPrice: it ends up wrapping a container. (We’ll address option 4 later.)

Composition

In order to compose two functions, the output of one must be the same type as the input of the other. With containers, we in general can only add containers, not take them away. So in our getPriceWithTax function above, we have getPrice returning a Maybe Int, and addTax taking a plain Int.

To make them compatible, one of them will have to change. Guess which? That’s right, we need to make addTax take a Maybe Int instead of an Int. Also, since we can’t remove the container in the middle, we end up returning a Maybe Int as well, which leads us to option 4 above.

Creating an option 4 function is where the Monadic bind function (>>=) comes in handy. It converts an contained -> contained function into a contained -> contained one. This gives us back composibility! But wait: addTax returns an uncontained value. No problem: we just add a return to make addTax return contained values.

Functors FTW

You might be thinking that it’s kind of silly to do business this way, and I’ll agree with you. What we really want is a way to convert totally uncontained functions (option 1) to totally contained ones (option 4). This is what Functors are for. A Functor has a single function, fmap, which does exactly that. So our code becomes:

getPriceWithTax3 item = addTax `fmap` getPrice item

Monads are still good

Now, there are times- many of them- when you’ll need the full power of Monads to deal with things. I hope to address that in a future post. But for now, the moral is: if you are using return with Monadic bind, you might want to consider fmap instead.

Advertisements

4 Responses to “Functors and Monads (containers)”

  1. gwern Says:

    “In a philosophy class in college, I remember learning the idea that in order to understand something, you can study it.”

    Wah? The following sentences seem to assume some clause or idea that didn’t make it into this sentence.

    ‘out topic today- things which might exist.’

    our.

    “The third option is unwrapping a contained value; it is not a topic for Monads or Functors, so ignore it for now. (If you care, look up fromJust.) ”

    Might mention why – fromJust is partial and thus evil.

  2. steez Says:

    “In order to learn that, you need to study the human body.”

    Does this have a name ? ( Explaining something by placing it into context / framework / system )

  3. powerofpi Says:

    fromJust is not “evil”. it is a very important concept.

    You have to look at it as a membership relation:

    If dealing with lists, this relation R relates x (of type A) and L (of type [A]), written x R L, if x is an element of L (elem funcion). Similarly for sets.

    In Maybe, x (of type A) is related to M (of type Maybe A) if and only if M = Just x

    Unfortunately, this is not really how fromJust is defined in Haskell, but the concept (of unwrapping something) is essential.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: