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
, then IO
has to be at the bottom of your monad transformer stack. Here's the usual mtl
-way of fixing things:
facHelper :: (MonadIO m, MonadState Integer m) => Integer -> m ()
-- same definition as before
factorial :: Integer -> IO Integer
factorial n = execStateT (facHelper n) 1
main :: IO ()
main = factorial 6 >>= print
This uses StateT Integer IO
instead of State 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 want MonadIO (StateT s Identity)
MonadIO m => MonadIO (StateT s m)
, and more specifically have MonadIO Identity => MonadIO (StateT s Identity)
, therefore want MonadIO Identity
MonadIO Identity