The Point Of Applicative Functors

A few weeks ago, we talked about Functors. In case you’ve forgotten, you might want to click that link and read the post, but long story short: Functors allow you to call functions that take “bare” values on “dressed up” values.

Functors had one big weakness though: you can only call a function that takes one argument. I justified this using function currying, but there is another way. Today, we’ll be talking about that other way: Applicative functors.

Let’s Recap

Functors use a function called fmap to call a function on a functor. This function is called on the value within the functor, and a new functor containing the result is returned. This looks like this:

Prelude> fmap show $ Just 1 Just "1"

I fmap‘d show over Just 1, which returns Just "1". Let’s try another one:

Prelude> fmap (+ 1) $ Just 1 Just 2

Here, we’ve used function currying to fmap (+ 2) over Just 1, which returns Just 2. This is all fine and good in a contrived situation such as adding 1 to 1, but what about the real world? As you probably know, you often don’t have some nice constants ready to use in your function. There must be a better way…

Applicatives To The Rescue

To put it in layman’s terms, Applicative Functors are the middle ground. Let’s take a look at another example:

Prelude> let example = fmap (+) $ Just 1 Prelude> :t example example :: Maybe (Integer -> Integer)

If we fmap (+) over Just 1, the result is a function that takes an Integer and returns an Integer, wrapped in a Maybe. To do anything useful with this, you need to find a way to call this function on something else. That’s what Applicative Functors do for us. Let’s take a look at part of the class definition of Applicative:

class (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b

We have two functions here: pure, which takes a value and wraps it in a minimal Functor, and (<*>), which takes a functor that contains a function, and calls it on another functor which contains a value, returning a value. In a nutshell, this allows us to call multi-argument functions with functors. There’s one more function exported by Control.Applicative that bears mentioning: (<$>). (<$>) is basically an alias for fmap that allows us to use fmap as infix:

Prelude> fmap (+1) $ Just 2 Just 3 Prelude> (+1) <$> Just 2 Just 3

As you can see, they function identically. Feel free to use (<$>), just know that it requires you to import Control.Applicative. Anyways, with that out of the way, here’s how you use an applicative. For this example, I will be adding 2 to 2. But 2 will be wrapped in a Maybe requiring extra steps:

Prelude> (+) <$> Just 2 <*> pure 2 Just 4

Let’s talk about what just happened. First, I fmap‘d (+) over Just 2 using the (<$>) operator. This returns a function that takes an Integer and returns an integer wrapped in a Maybe. Next I used the (<*>) operator to call that function on pure 2, which returns Just 4.

You may be wondering why I used pure 2 instead of Just 2. Sure, Just 2 would have worked, but pure 2 would have worked for any type of Applicative. Let’s take a look at another example:

Prelude> (+) <$> [1,2,99,100] <*> pure 2 [3,4,101,102]

As you can see, this is the exact same expression, except I used a List instead of a Maybe. This is where pure comes in handy. It allows you to write more generic code. pure works for any type of Applicative, therefore you needn’t concern yourself with what kind of Applicative is being passed into your function. You can rest easy knowing that it will work regardless.

Hopefully That All Made Sense

Applicatives are a pretty abstract concept, and it can be difficult to visualize how they can be useful. This is a topic that I don’t feel is documented very well. It’s a topic I had trouble with for a while.

Since I suspect that most readers will find this post with a google search term like “what is the point of applicative functors”, hopefully I’ve shed some light on the topic for you.

Leave a Reply

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

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

Google photo

You are commenting using your Google 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 )

Connecting to %s

%d bloggers like this: