Reputation: 2182
I'm testing a REST server. I hit it in the IO monad and simulate it in State Db
where Db
tracks the supposed state of the server. The following function is supposed to run both versions and compare the results...
check :: (Eq a, MonadState d s) => s a -> IO a -> s (IO Bool)
-- or: check :: (Eq a, MonadState d s, MonadIO i) => s a -> i a -> s (i Bool)
check _ _ = (return.return) False -- for now
but when I try it with these simplest possible functions ...
simReset :: State Db ()
realReset :: IO ()
reset :: StateT Db IO Bool
reset = check simReset realReset
I get this error:
Couldn't match expected type `Bool' with actual type `IO Bool'
Expected type: StateT Db IO Bool
Actual type: StateT Db IO (IO Bool)
In the return type of a call of `check'
In the expression: check simReset realReset
Why? And how do I fix it?
(This topic started here: Lift to fix the *inside* of a monad transformer stack)
Upvotes: 2
Views: 1545
Reputation: 2822
In your implementation, check
is going to return an IO Bool
regardless of what the state monad s
is. So, when you pass simReset
to check, which is a monadic action in the State Db
monad, the return value is going to be State Db (IO Bool)
.
How to fix it depends on what you're trying to do. Based on your implementation of reset
it seems like you're trying to interface with a transformer stack of the form StateT Db IO a
. In this case, you're describing a kind of program in the context of StateT Db IO
. There are two ways to go about this:
You can upgrade simReset
to have type MonadState Db s => s ()
(This shouldn't actually require any implementation change).
You can define an auxiliary function that "hoists" it into the proper monad
For example:
hoistState :: Monad m => State s a -> StateT s m a
hoistState prg = StateT $ \st -> return $ runState prg st
In either case, you'll probably want to keep the action that you're performing and the result in the same monad:
check :: (Eq a, MonadIO s, MonadState d s) => s a -> IO a -> s Bool
Then, with solution one, you have
reset = check simReset realReset
And with solution two you have:
reset = check (hoistState simReset) realReset
Upvotes: 2