# Pure functions and Monads

In today’s article I’ll be showing you a way to write pure functions that are Monad-friendly meaning that they are composable with Monadic contexts.

## Imports and Extensions

Let’s first get the imports and language extensions we’d be using for this article out of the way.

```
-- app.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad (forever)
import Control.Monad.Except (ExceptT, MonadError, catchError,
runExceptT, throwError)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.State (MonadState, StateT, evalStateT, get,
modify)
import Numeric (readDec)
import System.IO (BufferMode (NoBuffering),
hSetBuffering, stdout)
```

Cool. We can get started now!

## Pure functions and Monads

Say you want to write a pure function that has a possibility of failing. What type signature would you give it? One of the common ones is `Either String a`

. For example, let’s write a function that given a list of integers returns their average.

```
-- app.hs
avg' :: [Int] -> Either String Double
avg' [] = Left "Cannot take average of empty list"
avg' xs = Right $ fromIntegral (sum xs) / fromIntegral (length xs)
```

Functions with concrete return types such as `Either String a`

are not directly compatible with Monadic contexts other than `Either String`

. Suppose we have an `App`

monad for our application defined as follows.

```
-- app.hs
newtype App s m e a = App
{ unapp :: StateT s (ExceptT e m) a
} deriving (Functor, Applicative, Monad, MonadError e, MonadIO, MonadState s)
```

Calling our `avg'`

function from within `App`

context is not straightforward as the `Either e`

monad is not compatible with our `App`

monad out of the box. We’ll need to write a function that can lift a value of type `Either e a`

to `App s m e a`

type. Although writing such a function is possible, we can do better.

Instead of coding pure functions to concrete types, we can code them to typeclasses. The typeclass that abstracts the functionality offered by `Either e`

type is `MonadError e`

. Let’s redefine our average function so that it operates within `MonadError`

context.

```
-- app.hs
avg :: MonadError String m => [Int] -> m Double
avg [] = throwError "Cannot take average of empty list"
avg xs = pure $ fromIntegral (sum xs) / fromIntegral (length xs)
```

What did we change? Notice that we no longer return `Either String Double`

anymore. Instead, we return a `Double`

within a `MonadError String`

context. This `MonadError String`

context could be any `MonadError String`

instance! Notice that our `App s m e`

monad is an instance of `MonadError e`

making it compatible with our new `avg`

function when `e = String`

We have also replaced `Left`

with `throwError`

and `Right`

with `pure`

. For `Either e a`

monad the functionality is exactly the same as before but now our function is more generic.

Let’s confirm that `avg`

function does indeed work within different `MonadError String`

contexts.

```
-- ghci
λ> avg [1,2,3] :: Either String Double
Right 2.0
λ> let x = avg [1,2,3] :: Monad m => App s m String Double
λ> :t x
x :: Monad m => App s m String Double
```

Nice.

## Applying the knowledge

Let’s now write a simple application that would demonstrate the usefulness of Monad compatible pure functions. We will write a console app to print online averages of integers. The app will continuously keep asking the user for integer values and print the average of all values collected until now after each integer is read.

```
Enter an integer: 5
Current avg: 5.0
Enter an integer: 0
Current avg: 2.5
Enter an integer: abc
Could not parse "abc" to an int
Enter an integer: -2
Current avg: 1.0
```

We will use `State [Int]`

monad to store the current state of integers collected. So, our app will run in `App [Int] IO String`

monadic context. Let’s declare a type alias for this type for convenience.

```
type MyApp = App [Int] IO String
```

To run our `App s m e`

monad, we will need to unwrap and run all its transformers one by one.

```
-- app.hs
runapp :: Monad m => s -> App s m e a -> m (Either e a)
runapp s = runExceptT . flip evalStateT s . unapp
```

Now let’s define a `readInt`

function that will try to parse an integer from a string while handling any errors. For this we will use `readDec`

function from `Numeric`

module with some modifications.

```
-- app.hs
readInt :: MonadError String m => String -> m Int
readInt [] = throwError "Cannot read int from empty string"
readInt ('-':xs) = negate <$> readInt xs -- Handle negative integers
readInt xs = case readDec xs of
[(v, "")] -> pure v
_ -> throwError $ "Could not parse " <> show xs <> " to an int"
```

Note that we have defined `readInt`

function within `MonadError String`

context just like the `avg`

function.

Next, we define a function `getInt`

that will ask the user to input an integer, try to parse the input, and ask the user to input again if there was any error.

```
-- app.hs
getInt' :: MyApp Int
getInt' =
liftIO (putStr "Enter an integer: ")
*> liftIO getLine
>>= readInt
getInt :: MyApp Int
getInt = getInt' `catchError` \e -> (liftIO . putStrLn $ e) *> getInt
```

Note how we are able to call our pure `readInt`

function from within `MyApp`

monad without any lifting.

Alright, we have all the pieces we need. Now let’s define a loop that would ask the user for input and display the current averages.

```
-- app.hs
go :: MyApp ()
go = forever $ do
x <- getInt -- get int from user
modify (x:) -- prepend new int to our state
y <- avg =<< get -- compute the average of collected ints
liftIO $ putStrLn $ "Current avg: " <> show y
```

Once again, we are able to call pure `avg`

function from `MyApp`

context without having to perform any lift juggling.

And that’s it. We can now call `go`

from our `main`

function to start the app.

```
-- app.hs
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- so that everything is printed right away
runapp [] go >>= either print (const $ pure ())
```

## Conclusion and follow-ups

We saw today how pure functions can be made more generic so that they may be called from monadic contexts without much trouble.

You might have noticed that `MonadError String`

is not completely generic as it assumes the error type to be `String`

. We cannot get rid of the concrete error type as we need it to create the error value.

However, we can make our app monad an instance of `Bifunctor`

to easily convert a value of type `App s m e a`

to `App s m e' a`

. The function that does this is called first. I will cover Bifunctor in detail in a separate blog post.

## Appendix

Complete program is reproduced below if you want to copy it. ;)

```
-- app.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad (forever)
import Control.Monad.Except (ExceptT, MonadError, catchError,
runExceptT, throwError)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.State (MonadState, StateT, evalStateT, get,
modify)
import Numeric (readDec)
import System.IO (BufferMode (NoBuffering),
hSetBuffering, stdout)
avg :: (MonadError String m) => [Int] -> m Double
avg [] = throwError "Cannot take average of empty list"
avg xs = pure $ fromIntegral (sum xs) / fromIntegral (length xs)
readInt :: MonadError String m => String -> m Int
readInt [] = throwError "Cannot read int from empty string"
readInt ('-':xs) = negate <$> readInt xs -- Handle negative integers
readInt xs = case readDec xs of
[(v, "")] -> pure v
_ -> throwError $ "Could not parse " <> show xs <> " to an int"
newtype App s m e a = App
{ unapp :: StateT s (ExceptT e m) a
} deriving (Functor, Applicative, Monad, MonadError e, MonadIO, MonadState s)
type MyApp = App [Int] IO String
runapp :: Monad m => s -> App s m e a -> m (Either e a)
runapp s = runExceptT . flip evalStateT s . unapp
getInt' :: MyApp Int
getInt' =
liftIO (putStr "Enter an integer: ")
*> liftIO getLine
>>= readInt
getInt :: MyApp Int
getInt = getInt' `catchError` \e -> printError e *> getInt
where printError e = liftIO $ putStrLn e
go :: MyApp ()
go = forever $ do
x <- getInt
modify (x:)
y <- avg =<< get
liftIO $ putStrLn $ "Current avg: " <> show y
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
runapp [] go >>= either print (const $ pure ())
```