Monad Transformers (continued)

StateT

newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }

instance (Monad m) => Monad (StateT s m) where
 -- return :: a -> StateT s m a
    return a = StateT $ \s -> return (a, s)

 -- (>>=) :: StateT s m a -> (a -> StateT s m b) -> StateT s m b
    action >>= f = StateT $ \s -> do
      (a, s')  <- runStateT action s
      (b, s'') <- runStateT (f a) s'
      return (b, s'')

Free instances:

instance (Monad m) => Applicative (StateT s m) where
    pure = return
    (<*>) = ap

instance (Monad m) => Functor (StateT s m) where
    fmap f x = pure f <*> x

Plug in lift:

instance MonadTrans (StateT s) where
 -- lift :: Monad m => m a -> StateT s m a
    lift ma = StateT $ \s -> do
      a <- ma
      return (a, s)

Add monoid structure:

instance (Monad m, Alternative m) => Alternative (StateT s m) where
 -- empty :: StateT s m a
    empty = StateT $ \s -> empty

 -- (<|>) :: StateT s m a -> StateT s m a -> StateT s m a
    x <|> y = StateT $ \s -> runStateT x s <|> runStateT y s

Helper functions:

get :: Monad m => StateT s m s
get = StateT $ \s -> return (s, s)

put :: Monad m => s -> StateT s m ()
put s' = StateT $ \_ -> return ((), s')

modify :: Monad m => (s -> s) -> StateT s m ()
modify f = StateT $ \s -> return ((), f s)

evalStateT :: Monad m => StateT s m a -> s -> m a
evalStateT action init = fmap fst $ runStateT action init

execStateT :: Monad m => StateT s m a -> s -> m s
execStateT action init = fmap snd $ runStateT action init

Parser: Redux

type Parser a = StateT String [] a

char :: Char -> Parser Char
char c = StateT $ \s ->
  case s of
    []     -> []
    (a:as) -> [(c,as) | a == c]

Only difference compared to our Parser library before is the use of the StateT constructor instead of Parser. And now we get extra functionality, such as get, push, and modify.

> runStateT (char 'a') "aabbcc"
[('a',"abbcc")]
> runStateT (char 'a' >> get) "aabbcc"
[("abbcc","abbcc")]

State: Redux

type State s a = StateT s Identity a

Recall the trivial Identity wrapper monad.

newtype Identity a = Identity { runIdentity :: a }

instance Monad Identity where
    return  = Identity
    x >>= f = f $ runIdentity x

instance Applicative Identity where
    pure = return
    (<*>) = ap

instance Functor Identity where
    fmap f x = pure f <*> x

Helper functions:

evalState :: State s a -> s -> a
evalState action init = fst $ runIdentity $ runStateT action init

execState :: State s a -> s -> s
execState action init = snd $ runIdentity $ runStateT action init

state :: (s -> (a, s)) -> State s a
state f = StateT $ \s -> return $ f s

Back to Stack example. Same as before, since state and (>>=) take care of the pesky Identity wrappers:

type Stack = [Int]

push :: Int -> State Stack ()
pop :: State Stack Int

push i = state $ \stk -> ((), i:stk)

pop = state $ \stk ->
  case stk of
    i:is -> (i, is)
    []   -> (0, [])

Or:

push i = do
  is <- get
  put (i:is)

pop = do
  stk <- get
  case stk of
    []   -> return 0
    i:is -> do
      put is
      return i

NState

"Non-deterministic" stateful computations. Lists of possible results, try them all. Generalization of Parser with different types of state objects s.

type NState s a = StateT s [] a

Example:

type Turn = Either () ()
left      = Left ()
right     = Right ()

type Path = [Turn]

step :: NState Path ()
step =
  StateT $ \path ->
    [ ((), dir:path) | dir <- [left, right] ]

Note that step can't modify paths directly via modify because each existing path must be transformed into two paths. No problem:

step :: NState Path ()
step = modify (left:) <|> modify (right:)

walk :: NState Path ()
walk = do
  step
  step
  step

runWalk :: [Path]
runWalk =
  execStateT walk []

> runWalk
... 8 possible paths ...

NState with Randomness

NState computations represent multiple potential results. What if we want to really non-deterministically (i.e. randomly) choose just one?

type NState1 s a = State (s, StdGen) a
             --  = StateT (s, StdGen) Identity a
             -- ~= (s, StdGen) -> (a, (s, StdGen))

First define a function that converts an "all possible results" computation into "one random result":

chooseRandom1 :: NState s a -> NState1 s a
chooseRandom1 action = StateT $ \(s,g) ->
  let
    results = runStateT action s
    (i, g') = randomR (0, length results - 1) g
    (a, s') = results !! i
  in
  Identity $ (a, (s', g'))

And now perform a single random walk:

randomWalk1 :: NState1 Path ()
randomWalk1 = do
  chooseRandom1 step
  chooseRandom1 step
  chooseRandom1 step

runRandomWalk1 :: IO Path
runRandomWalk1 = do
  g <- newStdGen
  return $ fst $ execState randomWalk1 ([], g)

Or:

runRandomWalk1 =
  newStdGen >>= (,) [] >>> execState randomWalk1 >>> fst >>> return

> runRandomWalk1
> runRandomWalk1
> runRandomWalk1

A downside of our approach is that chooseRandom1 will crash if results == []. A version that handles errors more gracefully:

type NState01 s a = StateT (s, StdGen) Maybe a
              -- ~= (s, StdGen) -> Maybe (a, (s, StdGen))

chooseRandom01 :: NState s a -> NState01 s a
chooseRandom01 action = StateT $ \(s,g) -> do
  let results = runStateT action s
  guard $ length results > 0
  let (i, g') = randomR (0, length results - 1) g
  let (a, s') = results !! i
  Just (a, (s', g'))

randomWalk01 :: NState01 Path ()
randomWalk01 = do
  chooseRandom01 step
  chooseRandom01 step
  chooseRandom01 step

runRandomWalk01 :: IO (Maybe Path)
runRandomWalk01 =
  newStdGen >>= (,) [] >>> execStateT randomWalk01 >>> fmap fst >>> return

> runRandomWalk01
> runRandomWalk01
> runRandomWalk01

Source Files

results matching ""

    No results matching ""