The MonadReader class MonadReader defined using the FunctionalDependencies extension, which allows declarations of the type
class Monad m => MonadReader rm | m -> r where ...
This means that for any monad m value of r is uniquely determined. Therefore, you cannot have one monad m , which defines two different types of r . Without this, as a limitation, the compiler will not be able to introduce class usage checks.
The solution to this is to write your functions like
getA'sInt :: A -> Int getA'sInt = undefined getB'sString :: B -> String getB'sString = undefined foo :: (MonadReader A m) => m Int foo = do a <- asks getA'sInt return $ a + 1 bar :: (MonadReader B m) => m String bar = do b <- asks getB'sString return $ map toUpper b
Then just use the (A, B) tuple in your actual implementation:
baz :: Reader (A, B) (Int, String) baz = do a <- withReader fst foo b <- withReader snd bar return (a, b)
There is also withReaderT for more complex cases.
As an example of why he is not allowed to fold ReaderT s, consider the case
type App = ReaderT Int (Reader Int)
When you call ask , which Int are you Int to? It may seem obvious that for cases such as
type App = ReaderT A (Reader B)
the compiler should be able to determine what to use, but the problem is that here the ask function will be of type
ask :: App ???
Where ??? may be A or B You can get around this in another way, without using MonadReader directly and defining specific askA and askB :
type App = ReaderT A (Reader B) askA :: App A askA = ask askB :: App B askB = lift ask baz :: App (Int, String) baz = do a <- askA b <- askB return (getA'sInt a, getB'sString b)
But you can have a MonadReader A App , you also cannot have a MonadReader B App . This approach can be called an โexplicit liftโ, and it makes these functions specific to the App type and, therefore, less compositional.