# Purifying Code with Algebra

September 16, 2016

## Motivation

Side effects are the worst.

``````getNum :: IO Int
getNum = do
removeDirectoryRecursive "/"
return 42``````

## Objective

Use a GADT to get `IO` out of function types.

``{-# LANGUAGE GADTs #-}``

## Side effects and `IO`

A function might have side effects if it uses the `IO` type class.

## `IO` with a side effect

``````getNum2 :: IO Int
getNum2 = read <\$> getLine``````

Returns whatever value the user feels like providing

## `IO` without a side effect

``````getNum3 :: IO Int
getNum3 = return 42``````

Always returns 42

## Side effects and `IO`

A function can't have side effects if it doesn't use `IO`.

## A side effect without `IO`

``````getNum4 :: Int
getNum4 = read <\$> getLine``````

This will not compile

## Basic console IO

Let's start with a couple of library functions:

• `getLine :: IO String`
• `putStr :: String -> IO ()`

## `LameCalc`

``````calculator :: IO ()
calculator = do
putStr "Enter a number: "
x <- getLine
putStr "Enter another one: "
y <- getLine
let z = show (read x * read y)
putStr \$ x ++ " * " ++ y ++ " = " ++ z ++ "\n"``````

## Abstract the effects

``````data ConsoleIO a where
PutStr :: String -> ConsoleIO ()
GetLine :: ConsoleIO String``````

## Combinators as data types

``````data ConsoleIO a where
PutStr :: String -> ConsoleIO ()
GetLine :: ConsoleIO String``````
``````  Pure :: a -> ConsoleIO a
Ap :: ConsoleIO (a -> b) -> ConsoleIO a -> ConsoleIO b
Bind :: ConsoleIO a -> (a -> ConsoleIO b) -> ConsoleIO b``````

## Combinators as instances

``````instance Functor ConsoleIO where
fmap f s = Bind s (\a -> Pure \$ f a)

instance Applicative ConsoleIO where
pure a = Pure a
(<*>) f a = Ap f a

instance Monad ConsoleIO where
(>>=) a mb = Bind a mb``````

## Interpret the effects

``````class Effect x where
runEffect :: x a -> IO a

instance Effect ConsoleIO where
runEffect (Pure a) = return a
runEffect (Ap f a) = (runEffect f) <*> (runEffect a)
runEffect (Bind s fs) = (runEffect s) >>= (\a -> runEffect (fs a))
runEffect (PutStr s) = putStr s
runEffect GetLine = getLine``````

## Purify `calculator`

``````calculator1 :: IO ()
calculator1 = do
putStr "Enter a number: "
x <- getLine
putStr "Enter another one: "
y <- getLine
let z = show (read x * read y)
putStr \$ x ++ " * " ++ y ++ " = " ++ z ++ "\n"``````

## `PureCalc`

``````calculator2 :: ConsoleIO ()
calculator2 = do
PutStr "Enter a number: "
x <- GetLine
PutStr "Enter another one: "
y <- GetLine
let z = show (read x * read y)
PutStr \$ x ++ " * " ++ y ++ " = " ++ z ++ "\n"``````

Looks just like `calculator1`, but doesn't allow for side effects

## Demo

``````main :: IO ()
main = do
putStr "\nRunning calculator...\n"
calculator
putStr "\nRunning calculator2...\n"
runEffect calculator2``````

## Usage

These slides are literate Haskell. Try running them with Codedown:

``````\$ curl -s https://earldouglas.com/talks/gadt-io/slides.md | codedown haskell > gadt-io.hs
\$ runhaskell gadt-io.hs

Running calculator...
Enter a number: 6
Enter another one: 7
6 * 7 = 42

Running calculator2...
Enter a number: 6
Enter another one: 7
6 * 7 = 42``````