# Refactoring with Applicative - a small example in Haskell

Recently I came across a neat little refactoring example that I thought might prove useful, particularly to those starting out with Haskell.

#### Setting the scene

Say you have a file of comma-separated data, where each row is meant to represent the length, breadth and height of a box. Our task is to calculate the volume of each box. Simple, right? Well, like in real life, the data is not complete and sometimes values are missing. We want to handle these cases by simply not calculating a volume if any of the dimensions of the box are missing.

l,b,h (inputs) v (our output) --------------------------------- 4,5,1 20 1,, - 3,4, - 1,2,4 8

### Maybe volume

We'll work towards a function to calculate volume - something with a signature like this:

maybeVolume :: Maybe Length -> Maybe Breadth -> Maybe Height -> Maybe Volume

To illustrate the patterns at play in this example let's start with something a bit simpler. Let's work with Area before we get to Volume.

A great place to start is with some data types.

newtype Length = Length { getLength :: Int } deriving (Eq, Show)

newtype Width = Width { getWidth :: Int } deriving (Eq, Show)

newtype Area = Area { getArea :: Int } deriving (Eq, Show)

So with these types our pure function for `area`

is:

area :: Length -> Width -> Area area l w = Area $ (getLength l) * (getWidth w)

Working towards the problem at hand, let's extend this to
a function that handles missing values. `Maybe`

encodes the concept that a value may be missing. Following a simple pattern
matching approach gets us:

marea :: Maybe Length -> Maybe Width -> Maybe Area marea ml mw = case ml of Nothing -> Nothing Just l -> case mw of Nothing -> Nothing Just w -> Just $ area l w

This is fine but it is a little verbose and is ripe for some simplification.

Let's use the fact that `Maybe`

is a monad. The bind operation passes through Just,
while Nothing will force the result to always be Nothing. Using `do`

notation we
can rewrite `marea`

like so:

marea' :: Maybe Length -> Maybe Width -> Maybe Area marea' ml mw = do l <- ml w <- mw pure $ area l w

The shape of `marea'`

is exactly the motivating example for introducing applicative in
McBride and Patterson's, Applicative Programming with Effects.
Using applicative notation gets us to a one-liner.

marea'' :: Maybe Length -> Maybe Width -> Maybe Area marea'' ml mw = area <$> ml <*> mw

Armed with these examples, let us return to `maybeVolume.`

We will need a couple
of extra data types.

newtype Breadth = Breadth { getBreadth :: Int } deriving (Eq, Show)

newtype Volume = Volume { getVolume :: Int } deriving (Eq, Show)

And then our pure function for `volume`

is simply:

volume :: Length -> Width -> Breadth -> Volume volume l w b = Volume $ (getLength l) * (getWidth w) * (getBreadth b)

Skipping over the simplest version of the function (that uses pattern matching),
here are the monadic and applicative versions of `maybeVolume`

-- monadic approach mvolume :: Maybe Length -> Maybe Width -> Maybe Breadth -> Maybe Volume mvolume ml mw mb = do l <- ml w <- mw b <- mb pure $ volume l w b -- and with applicative mvolume' :: Maybe Length -> Maybe Width -> Maybe Breadth -> Maybe Volume mvolume' ml mw mb = volume <$> ml <*> mw <*> mb

To wrap up, we've seen how to take perfectly good code that uses
pattern matching, to code that uses idiomatic Haskell patterns like
monadic `do`

notation, and composable applicative notation. It's routine
to run into monads and applicatives in Haskell codebases and I found
simple examples like this one to help concretely connect with them.

Highly recommend the McBride and Patterson paper on applicatives by the way. Same goes for pretty much anything written by Conor McBride.