Why Applicative?
Tags: functional, haskell
Published: 2020-01-08 23:34:52

---
title: Why Applicative?
published: true
description: The basic intuition behind the Functor and Applicative type classes 
tags: functional,haskell
series: 100 Days Of FP
cover_image: https://thepracticaldev.s3.amazonaws.com/i/437cejvu71qhohri95x8.jpg
---

When learning about the different type classes in Haskell the one I struggled the most with was, by far, [Applicative](https://en.wikipedia.org/wiki/Applicative_functor).

### Functor

[Functor](https://en.wikipedia.org/wiki/Functor) is, at least to some extent, straightforward. We take any _unary_ functions and make them work on some (functor) context.

```hs
fmap :: Functor f => (a -> b) -> f a -> f b

-- or the infix version
(<$>) :: Functor f => (a -> b) -> f a -> f b
```

Say we have an increment function that works on `Int`s.

```hs
inc :: Int -> Int
```

```
λ> inc 1
2
```

By using `fmap` we can map any functor that contains an `Int`.

```
λ> fmap inc [1, 2, 3]
[2, 3, 4]
λ> fmap inc (Just 1)
Just 2
λ> fmap inc (Right 1)
Right 2
```

That becomes very clear when we align `fmap` with the function application operator.

```hs
($) ::                (a -> b) ->   a ->   b
(<$>) :: Functor f => (a -> b) -> f a -> f b

inc  $   1       --  2
inc <$> [1]      -- [2]
inc <$> (Just 1) -- Just 2
```

<figcaption>fmap is just function application inside a context.</figcaption>

_By the way, we'll use the infix version from now_.

### Applicative

In the case of applicative it's not clear. Or at least it took longer to click for me.

```hs
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
```

Comparing with `($)` doesn't really help. Why would I want to also have the function in the context?

```hs
($) ::                      (a -> b) ->   a ->   b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
```

We said functors allow to apply a _unary_ function in a context. But what happens if I want to apply a function with a higher arity?

```hs
add :: Int -> Int -> Int

add <$> (Just 1) -- ??
```

What does the repl says? 🦊

```
λ> :t add <$> (Just 1)
add <$> (Just 1) :: Maybe (Int -> Int)
```

`Maybe (Int -> Int)`? Yes, we saw that already in the `(<*>)` signature.

```hs
add <$> (Just 1) <*> (Just 2) -- Just 3
```

Let's dissect that 🔍

```hs
-- refresh these ones first :)
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

-- With Maybe applied (using TypeApplications extension)

(<$>) @Maybe :: (a -> b) -> Maybe a -> Maybe b

add :: Int -> Int -> Int

-- With Int applied in place of 'a'
(<$>) @Maybe @Int :: (Int -> b) -> Maybe Int -> Maybe b

-- With (Int -> Int) applied in place of 'b'
(<$>) @Maybe @Int @(Int -> Int)
--   (a   -> b)          -> Maybe a   -> Maybe b
  :: (Int -> Int -> Int) -> Maybe Int -> Maybe (Int -> Int)

add <$> (Just 1) :: Maybe (Int -> Int)
```

Here we see the first interesting thing. Since our `add` function takes two arguments (or to be more accurate one at a time). But we only provide one (the `Int` from `Maybe Int`), so it gets partially applied and returns a function (`Int -> Int`). So `b` is `Int -> Int`.

```hs
--     a  ->   b 
add :: Int -> (Int -> Int)
```

_Note that parens aren't actually needed since the arrow (`->`) is right associative._

That was the first part of the expression. We are missing the applicative.

```hs
(<*>) @Maybe :: Maybe (c -> d) -> Maybe c -> Maybe d

-- With Int in place of 'c'
(<*>) @Maybe @Int :: Maybe (Int -> b) -> Maybe Int -> Maybe b

-- And also Int in place of 'd'
(<*>) @Maybe @Int @Int
  :: Maybe (Int -> Int) -> Maybe Int -> Maybe Int
```

_Et voilà_

```hs
add :: Int -> Int -> Int
add <$> (Just 1) :: Maybe (Int -> Int)
add <$> (Just 1) <*> (Just 2) :: Maybe  Int
```

**Functor**: apply _unary_ functions in a context.

**Applicative**: apply _n-ary_ functions in a context.

_This is referred as [lift](https://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Applicative.html#v:liftA2) in Haskell._

And the whole point of applying functions in such contexts is the semantics associated with them. It might be for validation, optional values (without `null` :smirk:), lists or trees of items, running IO actions, parsers.

When the context is Maybe:

```
λ> add <$> (Just 1) <*> (Just 2)
Just 3
λ> add <$> Nothing <*> (Just 2)
Nothing
λ> add <$> (Just 1) <*> Nothing
Nothing
λ> add <$> Nothing <*> Nothing
Nothing
```

When the context is Either:

```
λ> add <$> (Right 1) <*> (Right 2)
Right 3
λ> add <$> (Left "err 1") <*> (Right 2)
Left "err 1"
λ> add <$> (Right 1) <*> (Left "err 2")
Left "err 2"
λ> add <$> (Left "err 1") <*> (Left "err 2")
Left "err 1"
```

When the context is List:

```
λ> add <$> [1, 2, 3] <*> [1, 2, 3]
[2,3,4,3,4,5,4,5,6]
λ> (,) <$> [1, 2, 3] <*> [1, 2, 3]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]
```

☝️ _More on that on the next one_.

### Conclusion

When learning functional programming all these type classes might seem scary. Developing a basic intuition of their purpose and usages is a big part of the process of getting comfortable using (and understanding) them.

I know there are more implications around Functor and Applicative that I have yet to discover. But as any learning process, it takes time. I'm sure more things will become clear and start sink in as I keep going.

But that's all for today. 

Happy and safe coding 🎉