I am trying to print inside the State monad by using liftIO function from MonadIO class:
import Control.Monad.State
import Control.Monad.IO.Class
import Control.Applicative
facHelper :: Integer -> State Integer ()
facHelper 0 = pure ()
facHelper n = do
currentState <- get
liftIO $ putStrLn $ "n = " ++ show currentState
modify (*n)
facHelper (n-1)
factorial :: Integer -> Integer
factorial n = snd (runState (facHelper n) 1)
main :: IO ()
main = print $ factorial 6
However, I get the error:
No instance for (MonadIO Data.Functor.Identity.Identity) arising from a use of ‘liftIO’
If I look up MonadIO class:
I see an instance:
MonadIO m => MonadIO (StateT s m)
And also see that State s is a type alias
type State s = StateT s Identity
So, in principle, I could use liftIO with State. Where is the problem ?
If you want to do
IO, thenIOhas to be at the bottom of your monad transformer stack. Here's the usualmtl-way of fixing things:This uses
StateT Integer IOinstead ofState Integer(i.e.StateT Integer Identity).If you're curious about the mechanical details of what went wrong with your approach:
MonadIO (State s)type State s = StateT s Identity, therefore wantMonadIO (StateT s Identity)MonadIO m => MonadIO (StateT s m), and more specifically haveMonadIO Identity => MonadIO (StateT s Identity), therefore wantMonadIO IdentityMonadIO Identity