Search code examples
haskellmonadsmonad-transformersstate-monadio-monad

combining IO monad with State Monad in example


I created simple evaluator for statements. I would like to do it using transformers - mix IO monad with State.
Could somebody explain how to do it ? It is something that I can't deal with it - transformers.

execStmt :: Stmt -> State (Map.Map String Int) ()
execStmt s = case s of
      SAssigment v e -> get >>= (\s -> put (Map.insert v (fromJust (runReader (evalExpM e) s)) s))
      SIf e s1 s2 -> get >>=  (\s -> case (fromJust (runReader (evalExpM e) s)) of
                                        0 -> execStmt s2
                                        _ -> execStmt s1
                              )
      SSkip -> return ()
      SSequence s1 s2 -> get >>= (\s -> (execStmt s1) >>= (\s' -> execStmt s2))
      SWhile e s1 -> get >>= (\s -> case (fromJust (runReader (evalExpM e) s)) of
                                        0 -> return ()
                                        _ -> (execStmt s1) >>= (\s' -> execStmt (SWhile e s1)))

execStmt' :: Stmt -> IO ()
execStmt' stmt =  putStrLn $ show $ snd $ runState (execStmt  stmt) Map.empty

Solution

  • Here's a basic program outline

    newtype StateIO s a = SIO {runSIO :: s -> IO (a, s)}
    
    put :: s -> StateIO s ()
    put s' = SIO $ \_s -> return ((), s')
    
    liftIO :: IO a -> StateIO s a
    liftIO ia = SIO $ \s -> do
        a <- ia
        return (a, s)
    
    instance Functor (StateIO s) where
        fmap ab (SIO sa) = SIO $ \s -> do
            (a, s') <- sa s
            let b = ab a
            return (b, s')
    
    instance Applicative (StateIO s) where
        pure a = SIO $ \s -> return (a, s)
        (SIO sab) <*> (SIO sa) = SIO $ \s -> do
            (ab, s' ) <- sab s
            (a , s'') <- sa  s'
            let b = ab a
            return (b, s')
    

    StateIO s a is something that takes an input state (of type s), and returns an IO action to produce something of type a as well as a new state.

    To check for understanding, do the following

    • Write a value of get :: StateIO s s which retrieves the state.
    • Write an instance for Monad (StateIO s) (it will be similar to the code above).
    • Finally, and this is the big step to understanding transformers, is to define newtype StateT m s a = StateT {run :: s -> m (a, s)}, and translate the above code to that (with the constraint Monad m). This will show you how monad transformers work.