Let's say I have these two functions:
errorm :: ( MonadError String m ) => Bool -> m Int
errorm cond = if cond then return 1 else throwError "this is an error"
errorms :: ( MonadError String m ) => Bool -> m String
errorms cond = if cond then return "works" else throwError "does not work"
As you can see, one returns a string in the safe case, while the other returns an int
I now want to use them together within another monad. Trivially:
errErr :: MonadError String m => Bool -> Bool -> m (Int, String)
errErr b1 b2 = do
a <- errorm b1
b <- errorms b2
return (a,b)
The function signature here is derived by the GHC, and I am not sure how to use this function. I tried this:
runErrorT ( runErrorT ( errErr1 True True ) ) -- should give me (Right 1, Right "works")
But instead it gives me:
Ambiguous type variable `e0' in the constraint:
(Error e0) arising from a use of `errErr1'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `runErrorT', namely `(errErr1 True True)'
In the first argument of `runErrorT', namely
`(runErrorT (errErr1 True True))'
In the expression: runErrorT (runErrorT (errErr1 True True))
In general, this is just one instance of my problem. I feel like I am not grasping how exactly to stack two monadT that are of the same class, but have different type parameters. Another example might be stacking the pair of a functions:
f :: ( MonadState Int m ) => m ()
g :: ( MonadState String m ) => m ()
---------------------------------------------------- Update ----------------------------------------------------
Per Daniel's comment below, I added a concrete instance of functions f and g from above. But thanks to Tikhon's answer, I think I figured it out.
type Counter = Int
type Msg = String
incr :: (MonadState Counter m) => Counter -> m ()
incr i = modify (+i)
addMsg :: ( MonadState Msg m ) => Msg -> m()
addMsg msg = modify ( ++ msg )
incrMsg:: (MonadTrans t, MonadState Msg m, MonadState Counter (t m)) => t m ()
incrMsg = do
lift . addMsg $ "one"
incr 1
return ()
incrMsgt = runIdentity $ runStateT ( runStateT incrMsg 1 ) "hello" :: (((), Int), String)
runErrorT
, both,errorm
anderrorms
happily run in the same monad:runErrorT ( errErr True True ) :: [Either String (Int,String)]
produces[Right (1,"works")]
. – Daniel FischerMonadError String m
constraints, and only the result type parameter is different. You don't need any stacking for that. In the general case alluded to at the bottom, you do, because there the state type parameter is different. Can you clarify what problem exactly you are trying to solve? – Daniel Fischer