Commit 9f19167a authored by Christoph Ruegge's avatar Christoph Ruegge
Browse files

some more changes to monads 2

parent 905efba4
%% Cell type:markdown id: tags:
# Monads II
## The State monad
Imperative languages have global variables, Haskell has similar functinality with *state monads*.
%% Cell type:code id: tags:
``` haskell
newtype State s a = State { runState :: s -> (a, s) }
```
%% Cell type:markdown id: tags:
- `State s a` is a value of type `a`, obtained by a stateful computation with state of type `s` (e.g. a record type holding all "global" variables).
- `runState` takes an initial state and outputs the value *and a new state*.
- Compare this to the intuition for the `IO` monad in the previous lecture: `IO == State RealWorld`.
- For every `s`, `State s` is a monad:
%% Cell type:code id: tags:
``` haskell
import Control.Monad
instance Functor (State s) where
fmap = liftM
instance Applicative (State s) where
pure = return
(<*>) = ap
instance Monad (State s) where
return x = State $ \s -> (x, s)
x >>= f = State $ \s -> let (x', s') = runState x s
in runState (f x') s'
```
%% Cell type:markdown id: tags:
**`get`** gets the current state
%% Cell type:code id: tags:
``` haskell
get :: State s s
get = State $ \s -> (s, s)
```
%% Cell type:markdown id: tags:
**`gets`** is a variation that applies a function to the state before returning. Useful e.g. if the state is a record type.
%% Cell type:code id: tags:
``` haskell
gets :: (s -> a) -> State s a
gets f = State $ \s -> (f s, s)
```
%% Cell type:markdown id: tags:
**`put`** sets a new state
%% Cell type:code id: tags:
``` haskell
put :: s -> State s ()
put s = State $ const ((), s)
```
%% Cell type:markdown id: tags:
**`modify`** applies a function to the state
%% Cell type:code id: tags:
``` haskell
modify :: (s -> s) -> State s ()
modify f = State $ \s -> ((), f s)
```
%% Cell type:markdown id: tags:
**`evalState`**, **`execState`** short forms for computing only the resulting value / resulting state, resp.
%% Cell type:code id: tags:
``` haskell
evalState :: State s a -> s -> a
evalState m s = fst $ runState m s
execState :: State s a -> s -> s
execState m s = snd $ runState m s
```
%% Cell type:markdown id: tags:
### Example: Turing machine
- infinitely long tape with letters (here: integers), initialized to 0
- read/write head that in each step:
- reads a letter
- writes a new letter
- moves left, right or stays
- the machine has an internal state indexed by (a finite subset of the) integers that can influence decisions and change in each step
- the machine stops if a special internal state (the final state) is reached
%% Cell type:code id: tags:
``` haskell
-- for clarity
type Letter = Integer
type MState = Integer
-- state type for the State monad, includes state of tape and internal state of machine
data Turing = Turing { left :: [Letter]
, cur :: Letter
, right :: [Letter]
, machineState :: MState
}
-- where to move next
data Move = GoLeft | GoRight | Stay
-- the transition function of the machine
type TuringFunc = MState -> Letter -> (MState, Letter, Move)
-- this is conceptually the same as
-- type TuringFunc = State (MState, Letter) Move
-- but we do not use that here
turingInit :: Turing
turingInit = Turing { left = repeat 0, right = repeat 0, machineState = 0 }
runTuring :: TuringFunc -> MState -> State Turing Letter
runTuring func final = do
c <- gets cur
ms <- gets machineState
if ms == final
then return c
else do
let (ms', c', mv) = func ms c
modify $ \t -> t { cur = c', machineState = ms' }
c <- gets cur
l <- gets left
r <- gets right
case mv of
Stay -> return ()
GoLeft -> modify $ \t -> t { left = tail l
, cur = head l
, right = c:r
}
GoRight -> modify $ \t -> t { left = c:l
, cur = head r
, right = tail r
}
runTuring func final
execTuring :: TuringFunc -> MState -> Turing
execTuring func final = execState (runTuring func final) turingInit
evalTuring :: TuringFunc -> MState -> Letter
evalTuring func final = evalState (runTuring func final) turingInit
```
%% Cell type:markdown id: tags:
`State` is defined in `Control.Monad.State`. The actual definition is more involved than the one above (more below).
Restart the kernel before evaluating the next cell.
%% Cell type:code id: tags:
``` haskell
import Control.Monad.State
:info State
```
%%%% Output: display_data
%% Cell type:markdown id: tags:
## Reader and Writer
%% Cell type:markdown id: tags:
Defined in `Control.Monad.Reader` and `Control.Monad.Writer`.
- Reader can be viewed as a read-only version of the State monad.
```haskell
newtype Reader r a = { runReader :: r -> a }
```
- Writer can be used e.g. for logging or incrementally building up a result
```haskell
newtype Writer w a = { runWriter :: (a, w) }
```
`Writer w` is a monad only if `w` is a `Monoid`; `(>>=)` uses the monoid structure (exercise: how?) to combine, e.g. append, the result.
- Some operations (exercise: what do they do, how can they be defined?)
```haskell
tell :: w -> Writer w ()
listen :: Writer w a -> Writer w (a, w)
ask :: Reader r r
local :: (r -> r) -> Reader r a -> Reader r a
```
%% Cell type:markdown id: tags:
## Nesting monads: Monad Transformers
### Task
*Read lines of input unil an empty line is encountered. Return a list of all lines. Do this using a `Writer`.*
How should `Writer` and `IO` be combined?
#### `Writer [IO String] ()`
- Need to use `sequence . execWriter` to get from `[IO String]` to `IO [String]` at the end.
- Before that, we do not have access to the `String` wrapped in `IO` (since the action has not actually been performed yet) ...
- ... so it is not possible to test for the empty string.
#### `Writer (IO [String]) ()`
- `IO [a]` is not a `Monoid` (but it is easy to write an instance).
- `tell` expects an `IO [String]`, which we read using `getLine :: IO String` like
```haskell
do x <- getLine
return [x]
```
- The `String` `x` is only available inside this `do` block ...
- ... so testing for emptiness can not influence the surrounding function.
#### `IO (Writer [String] ())`
- Even without emptiness test, this won't work:
```haskell
readAndTell :: IO (Writer [String] ())
readAndTell = do
x <- getLine
return $ do
tell [x]
readAndTell -- error: we can not perform IO inside the Writer!
```
- If we performe the recursive call outside the `Writer`, we loose the line we just `tell`'d
```haskell
readAndTell :: IO (Writer [String] ())
readAndTell = do
x <- getLine
return $ tell [x]
readAndTell -- works, but [x] is gone
```
- We have to explictly carry around the `Writer` to keep the state:
%% Cell type:code id: tags:
``` haskell
import Control.Monad.Writer
readAndTell' :: Writer [String] () -> IO (Writer [String] ())
readAndTell' w = do
x <- getLine
if x == "" then return w
else readAndTell' $ w >> tell [x]
readAndTell :: IO [String]
readAndTell = liftM execWriter $ readAndTell' (return ())
```
%% Cell type:markdown id: tags:
- However, this is no better than explicitly passing the `[String]` list around
%% Cell type:markdown id: tags:
### Better solution using `WriterT`
%% Cell type:code id: tags:
``` haskell
readAndTell' :: WriterT [String] IO ()
readAndTell' = do
x <- lift getLine
when (x /= "") $ do
tell [x]
readAndTell'
readAndTell :: IO [String]
readAndTell = execWriterT readAndTell'
```
%% Cell type:markdown id: tags:
- `WriterT [String]` is similar to `Writer [String]`, but wraps another `Monad` instead of a plain type.
%% Cell type:code id: tags:
``` haskell
:kind Writer [String]
:kind WriterT [String]
```
%%%% Output: display_data
%%%% Output: display_data
%% Cell type:markdown id: tags:
- The result `WriterT [String] IO` is a `Monad` again.
- Inside this monad, `tell` can be used, but also `IO` performed using `lift` to access the wrapped monad.
### Monad transformer type classes
- `lift` belongs to the `MonadTrans` class
```haskell
class MonadTrans t where
lift :: Monad m => m a -> t m a
```
- `WriterT w` is a `MonadTrans`
```haskell
instance Monoid w => MonadTrans (WriterT w)
```
- `WriterT w m` is also a monad
```haskell
instance (Monoid w, Monad m) => Monad (WriterT w m)
```
Similar things hold for `ReaderT`, `StateT`, `MaybeT`, ... Moreover,
```haskell
type Writer w = WriterT w Identity
```
### Stacked transformers
- Since transformed monads are monads themselves, they can be transformed again:
```haskell
type App = ReaderT AppConfig (WriterT AppLog (StateT AppState IO))
```
- There is no `IOT`, so `IO` has to be at the bottom of the stack if it is needed.
- Operations need to be lifted using `lift` for the first level, `lift . lift` for the second, etc...
- For `IO`, there is also `liftIO` which lifts an `IO` action from the bottom of the stack.
### Getting rid of `lift`
Many of the standard monad transformers have their functionality split out into type classes, e.g. instead of
```haskell
tell :: w -> Writer w ()
```
or
```haskell
tell :: Monad m => w -> WriterT w m ()
```
the signature is
%% Cell type:code id: tags:
``` haskell
:t tell
```
%%%% Output: display_data
%% Cell type:markdown id: tags:
and there is an instance
```haskell
instance (Monoid w, Monad m) => MonadWriter w (WriterT w m)
```
There are also instances like
```haskell
instance MonadWriter w m => MonadWriter w (ReaderT r m)
```
Therefore, `ReaderT r (WriterT w IO)` is a `MonadWriter w` and can use `tell` directly, without `lift`.
The same holds for classes like
- `MonadReader`
- `MonadState`
- `MonadIO` (which provides `liftIO` described above)
Some classes are missing for some reason, like e.g. `MonadMaybe`.
%% Cell type:markdown id: tags:
### Example: MaybeT
*Read two numbers from stdin and print their sum, or "Error" if an input is not a number.*
Use `readMaybe :: Read a => String -> Maybe a` from `Text.Read`.
%% Cell type:code id: tags:
``` haskell
import Text.Read hiding (lift)
getMaybe :: Read a => IO (Maybe a)
getMaybe = do
x <- getLine
return $ readMaybe x
readAndAdd = do
x <- getMaybe
y <- getMaybe
let z = liftM2 (+) x y
maybe (putStrLn "Error") print z
```
%% Cell type:markdown id: tags:
- Works, but if the first input is an error, the second one is still read.
- We work in the `IO` monad and do not have automatic termination on error that `Maybe` implements.
- Back to manual checking:
%% Cell type:code id: tags:
``` haskell
import Data.Maybe
readAndAdd' = do
x <- getMaybe
z <- if isJust x then do
y <- getMaybe
return $ liftM2 (+) x y
else return Nothing
maybe (putStrLn "Error") print z
```
%% Cell type:markdown id: tags:
We essentially have to implement `Maybe`'s `(>>=)` on top of `IO`. That is precisely what `MaybeT IO` is for.
%% Cell type:code id: tags:
``` haskell
import Control.Monad.Trans.Maybe
import Text.Read hiding (lift)
-- we need to wrap the return value of readMaybe using
-- MaybeT :: m (Maybe a) -> MaybeT m a
readMaybeT :: (Monad m, Read a) => String -> MaybeT m a
readMaybeT = MaybeT . return . readMaybe
getMaybeT :: Read a => MaybeT IO a
getMaybeT = do
x <- liftIO getLine
readMaybeT x
readAndAddT = do
z <- runMaybeT $ do
-- here we can use IO *and* automatically break
-- if reading x fails
x <- getMaybeT
y <- getMaybeT
return $ x + y
maybe (putStrLn "Error") print z
```
%% Cell type:markdown id: tags:
### Implementation of `MaybeT`
%% Cell type:code id: tags:
``` haskell
data MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
import Control.Monad
instance Monad m => Functor (MaybeT m) where
fmap = liftM
instance Monad m => Applicative (MaybeT m) where
pure = return
(<*>) = ap
instance Monad m => Monad (MaybeT m) where
x >>= f = MaybeT $ do
x' <- runMaybeT x
case x' of
Just a -> runMaybeT $ f a
Nothing -> return Nothing
return = MaybeT . return . Just
```
%% Cell type:markdown id: tags:
Compare with `Maybe`
%% Cell type:code id: tags:
``` haskell
instance Monad Maybe where
x >>= f = case x of
Just a -> f a
Nothing -> Nothing
return = Just
```
%% Cell type:markdown id: tags:
Except for wrapping (`MaybeT`) and unwrapping (`runMaybeT`), the main differences are the additional `return` and the `(>>=)` (which is implicit in `<-` in the `do`-block) inside the wrapped monad `m`.
Finally, make `MaybeT` a `MonadTrans`.
%% Cell type:code id: tags:
``` haskell
import Control.Monad.Trans
instance MonadTrans MaybeT where
-- lift :: Monad m => m a -> MaybeT m a
lift x = MaybeT $ do
x' <- x
return (Just x')
```
%% Cell type:markdown id: tags:
## Random numbers
- Functions returning random numbers are not *referentially transparent*: they return a different result each time they are called.
- Many languages carry around a some state in a global variable that is implicitly updated on generating a number.
- Conceptually, if `g` is the global state, random numbers are generated by a function `g -> (a, g)` (note the similarity to the `State` monad).
### `RandomGen`
The typeclass `RandomGen` in `System.Random` provides an interface for this:
```haskell
class RandomGen where
next :: g -> (Int, g)
genRange :: g -> (Int, Int)
split :: g -> (g, g)
```
- There is an implicit global `RandomGen` of type `StdGen`, which can be accessed from the `IO` monad:
**getStdGen**: <br />
```haskell
getStdGen :: IO StdGen
```
**setStdGen**: <br />
```haskell
setStdGen :: StdGen -> IO ()
```
**getStdRandom**: <br />
```haskell
getStdRandom :: forall a. (StdGen -> (a, StdGen)) -> IO a
```
**mkStdGen**: <br />
```haskell
mkStdGen :: Int -> StdGen
```
### `Random`
The typeclass `Random` is an interface for types that can be generated randomly. All number types implement this typeclass. Some functions:
**randomR**: <br />
generate a random numbers in a given range
```haskell
randomR :: forall a g. (RandomGen g, Random a) => (a, a) -> g -> (a, g)
```
%% Cell type:code id: tags:
``` haskell
import System.Random
getStdRandom $ randomR (0::Double, 1)
```
%%%% Output: display_data
%% Cell type:markdown id: tags:
**randomRs**: <br />
iterated version: generate an infinte list of numbers
```haskell
randomRs :: forall a g. (RandomGen g, Random a) => (a, a) -> g -> [a]
```
**randomRIO**: <br />
convenience function using the global StdGen
```haskell
randomRIO :: forall a. Random a => (a, a) -> IO a
```
**random** etc: <br />
use type dependent default ranges
```haskell
random :: forall a g. (RandomGen g, Random a) => g -> (a, g)
randoms :: forall a g. (RandomGen g, Random a) => g -> [a]
randomIO :: forall a. Random a => IO a
```
### Other random distributions
... are provided by packages/modules, e.g.
%% Cell type:code id: tags:
``` haskell