2 Jan 2017

As of March 2020, School of Haskell has been switched to read-only mode.

NOTE This content is now being maintained on my personal blog. The content here may be out of date.

Let's start off with a very simple problem. We want to let a user input his/her birth year, and tell him/her his/her age in the year 2020. Using the function `read`, this is really simple:

``````main = do
year <- getLine
putStrLn \$ "In 2020, you will be: " ++ show (2020 - read year)``````

If you run that program and type in a valid year, you'll get the right result. However, what happens when you enter something invalid?

``````Please enter your birth year
hello

The problem is that the user input is coming in as a `String`, and `read` is trying to parse it into an `Integer`. But not all `String`s are valid `Integer`s. `read` is what we call a partial function, meaning that under some circumstances it will return an error instead of a valid result.

A more resilient way to write our code is to use the `readMay` function, which will return a `Maybe Integer` value. This makes it clear with the types themselves that the parse may succeed or fail. To test this out, try running the following code:

``````import Safe (readMay)

main = do
-- We use explicit types to tell the compiler how to try and parse the
-- string.
print (readMay "1980" :: Maybe Integer)
print (readMay "hello" :: Maybe Integer)
print (readMay "2000" :: Maybe Integer)
print (readMay "two-thousand" :: Maybe Integer)``````

So how can we use this to solve our original problem? We need to now determine if the result of `readMay` was successful (as `Just`) or failed (a `Nothing`). One way to do this is with pattern matching:

``````import Safe (readMay)

main = do
yearString <- getLine
Nothing -> putStrLn "You provided an invalid year"
Just year -> putStrLn \$ "In 2020, you will be: " ++ show (2020 - year)``````

## Decoupling code

This code is a bit coupled; let's split it up to have a separate function for displaying the output to the user, and another separate function for calculating the age.

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided an invalid year"
Just age -> putStrLn \$ "In 2020, you will be: " ++ show age

yearToAge year = 2020 - year

main = do
yearString <- getLine
let maybeAge =
Nothing -> Nothing
Just year -> Just (yearToAge year)
displayAge maybeAge``````

This code does exactly the same thing as our previous version. But the definition of `maybeAge` in the `main` function looks pretty repetitive to me. We check if the parse year is `Nothing`. If it's `Nothing`, we return `Nothing`. If it's `Just`, we return `Just`, after applying the function `yearToAge`. That seems like a lot of line noise to do something simple. All we want is to conditionally apply `yearToAge`.

## Functors

Fortunately, we have a helper function to do just that. `fmap`, or functor mapping, will apply some function over the value contained by a functor. `Maybe` is one example of a functor, another common one is a list. In the case of `Maybe`, `fmap` does precisely what we described above. So we can replace our code with:

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided an invalid year"
Just age -> putStrLn \$ "In 2020, you will be: " ++ show age

yearToAge year = 2020 - year

main = do
yearString <- getLine
let maybeAge = fmap yearToAge (readMay yearString)
displayAge maybeAge``````

Our code definitely got shorter, and hopefully a bit clearer as well. Now it's obvious that all we're doing is applying the `yearToAge` function over the contents of the `Maybe` value.

So what is a functor? It's some kind of container of values. In `Maybe`, our container holds zero or one values. With lists, we have a container for zero or more values. Some containers are even more exotic; the `IO` functor is actually providing an action to perform in order to retrieve a value. The only thing functors share is that they provide some `fmap` function which lets you modify their contents.

## do-notation

We have another option as well: we can use do-notation. This is the same way we've been writing our `main` function in so far. That's because- as we mentioned in the previous paragraph- `IO` is a functor as well. Let's see how we can change our code to not use `fmap`:

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided an invalid year"
Just age -> putStrLn \$ "In 2020, you will be: " ++ show age

yearToAge year = 2020 - year

main = do
yearString <- getLine
let maybeAge = do
return \$ yearToAge yearInteger
displayAge maybeAge``````

Inside the `do-`block, we have the slurp operator `<-`. This operator is special for do-notation, and is used to pull a value out of its wrapper (in this case, `Maybe`). Once we've extracted the value, we can manipulate it with normal functions, like `yearToAge`. When we complete our do-block, we have to return a value wrapped up in that container again. That's what the `return` function does.

do-notation isn't available for all `Functor`s; it's a special feature reserved only for `Monad`s. `Monad`s are an extension of `Functor`s that provide a little extra power. We're not really taking advantage of any of that extra power here; we'll need to make our program more complicated to demonstrate it.

## Dealing with two variables

It's kind of limiting that we have a hard-coded year to compare against. Let's fix that by allowing the user to specify the "future year." We'll start off with a simple implementation using pattern matching and then move back to do notation.

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
let maybeAge =
Nothing -> Nothing
Just birthYear ->
Nothing -> Nothing
Just futureYear -> Just (futureYear - birthYear)
displayAge maybeAge``````

OK, it gets the job done... but it's very tedious. Fortunately, do-notation makes this kind of code really simple:

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
let maybeAge = do
return \$ yearDiff futureYear birthYear
displayAge maybeAge``````

This is very convenient: we've now slurped our two values in our do-notation. If either parse returns `Nothing`, then the entire do-block will return `Nothing`. This demonstrates an important property about `Maybe`: it provides short circuiting.

Without resorting to other helper functions or pattern matching, there's no way to write this code using just `fmap`. So we've found an example of code that requires more power than `Functor`s provide, and `Monad`s provide that power.

## Partial application

But maybe there's something else that provides enough power to write our two-variable code without the full power of `Monad`. To see what this might be, let's look more carefully at our types.

We're working with two values: `readMay birthYearString` and ```readMay futureYearString```. Both of these values have the type `Maybe Integer`. And we want to apply the function `yearDiff`, which has the type ```Integer -> Integer -> Integer```.

If we go back to trying to use `fmap`, we'll seemingly run into a bit of a problem. The type of `fmap`- specialized for `Maybe` and `Integer`- is `(Integer -> a) -> Maybe Integer -> Maybe a`. In other words, it takes a function that takes a single argument (an `Integer`) and returns a value of some type `a`, takes a second argument of a `Maybe Integer`, and gives back a value of type `Maybe a`. But our function- `yearDiff`- actually takes two arguments, not one. So `fmap` can't be used at all, right?

Not true actually. This is where one of Haskell's very powerful features comes into play. Any time we have a function of two arguments, we can also look at is as a function of one argument which returns a function. We can make this more clear with parentheses:

``````yearDiff :: Integer -> Integer -> Integer
yearDiff :: Integer -> (Integer -> Integer)``````

So how does that help us? We can look at the `fmap` function as:

``````fmap :: (Integer -> (Integer -> Integer))
-> Maybe Integer -> Maybe (Integer -> Integer)``````

Then when we apply `fmap` to `yearDiff`, we end up with:

``fmap yearDiff :: Maybe Integer -> Maybe (Integer -> Integer)``

That's pretty cool. We can apply this to our `readMay futureYearString` and end up with:

``fmap yearDiff (readMay futureYearString) :: Maybe (Integer -> Integer)``

That's certainly very interesting, but it doesn't help us. We need to somehow apply this value of type `Maybe (Integer -> Integer)` to our ```readMay birthYearString``` of type `Maybe Integer`. We can do this with do-notation:

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
let maybeAge = do
yearToAge <- fmap yearDiff (readMay futureYearString)
return \$ yearToAge birthYear
displayAge maybeAge``````

We can even use `fmap` twice and avoid the second slurp:

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
let maybeAge = do
yearToAge <- fmap yearDiff (readMay futureYearString)
displayAge maybeAge``````

But we don't have a way to apply our `Maybe (Integer -> Integer)` function to our `Maybe Integer` directly.

## Applicative functors

And now we get to our final concept: applicative functors. The idea is simple: we want to be able to apply a function which is inside a functor to a value inside a functor. The magic operator for this is `<*>`. Let's see how it works in our example:

``````import Safe (readMay)
import Control.Applicative ((<*>))

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
let maybeAge =
displayAge maybeAge``````

In fact, the combination of `fmap` and `<*>` is so common that we have a special operator, `<\$>`, which is a synonym for `fmap`. That means we can make our code just a little prettier:

``````import Safe (readMay)
import Control.Applicative ((<\$>), (<*>))

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
-- show
let maybeAge = yearDiff
-- /show
displayAge maybeAge``````

Notice the distinction between `<\$>` and `<*>`. The former uses a function which is not wrapped in a functor, while the latter applies a function which is wrapped up.

## So we don't need Monads?

So if we can do such great stuff with functors and applicative functors, why do we need monads at all? The terse answer is context sensitivity: with a monad, you can make decisions on which processing path to follow based on previous results. With applicative functors, you have to always apply the same functions.

Let's give a contrived example: if the future year is less than the birth year, we'll assume that the user just got confused and entered the values in reverse, so we'll automatically fix it by reversing the arguments to `yearDiff`. With do-notation and an if statement, it's easy:

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
let maybeAge = do
return \$
if futureYear < birthYear
then yearDiff birthYear futureYear
else yearDiff futureYear birthYear
displayAge maybeAge``````

## Exercises

1. Implement `fmap` using `<*>` and `return`.

``````import Control.Applicative ((<*>), Applicative)
import qualified Prelude

fmap :: (Applicative m, Monad m) => (a -> b) -> (m a -> m b)
-- show
fmap ... ... = FIXME
-- /show

main =
case fmap (Prelude.+ 1) (Prelude.Just 2) of
Prelude.Just 3 -> Prelude.putStrLn "Good job!"
_ -> Prelude.putStrLn "Try again"``````
2. How is `return` implemented for the `Maybe` monad? Try replacing `return` with its implementation in the code above.

``````-- show
returnMaybe = FIXME
-- /show

main
| returnMaybe "Hello" == Just "Hello" = putStrLn "Correct!"
| otherwise = putStrLn "Incorrect, please try again"``````
3. `yearDiff` is really just subtraction. Try to replace the calls to `yearDiff` with explicit usage of the `-` operator.

``````import Safe (readMay)

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

main = do
birthYearString <- getLine
putStrLn "Please enter some year in the future"
futureYearString <- getLine
let maybeAge = do
return \$
-- show
if futureYear < birthYear
then yearDiff birthYear futureYear
else yearDiff futureYear birthYear
-- /show
displayAge maybeAge``````
4. It's possible to write an applicative functor version of the auto-reverse-arguments code by modifying the `yearDiff` function. Try to do so.

``````import Safe (readMay)
import Control.Applicative ((<\$>), (<*>))

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

-- show
yearDiff futureYear birthYear = FIXME
-- /show

main
| yearDiff 5 6 == 1 = putStrLn "Correct!"
| otherwise = putStrLn "Please try again"``````
• Now try to do it without modifying `yearDiff` directly, but by using a helper function which is applied to `yearDiff`.

``````import Safe (readMay)
import Control.Applicative ((<\$>), (<*>))

displayAge maybeAge =
case maybeAge of
Nothing -> putStrLn "You provided invalid input"
Just age -> putStrLn \$ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear
-- show
yourHelperFunction f ...
-- /show

main
| yourHelperFunction yearDiff 5 6 == 1 = putStrLn "Correct!"
| otherwise = putStrLn "Please try again"``````