Your mainProgram signature mainProgram problematic because the MonadReader class contains a functional dependency MonadReader rm | m -> r MonadReader rm | m -> r . This essentially means that one particular type cannot have an instance of MonadReader for several different types. Therefore, when you say that type m has both MonadReader MessageCase and MonadReader LogConfig , it goes against declaring the dependency.
The simplest solution is to change mainProgram to a non-generic type:
mainProgram :: ReaderT MessageCase (ReaderT LogConfig IO) () mainProgram = do input <- getUserInput lift $ doLog input liftIO $ putStrLn $ "Entry logged: " ++ input
It also requires an explicit lift for doLog .
Now you can run mainProgram by running each ReaderT separately, for example:
main :: IO () main = do let messageCase = undefined :: MessageCase logConfig = undefined :: LogConfig runReaderT (runReaderT mainProgram messageCase) logConfig
If you want to have a common function that uses two different instances of MonadReader , you need to make it explicit in the signature that one reader is a monad transformer on top of another reader.
mainProgram :: (MonadTrans mt, MonadReader MessageCase (mt m), MonadReader LogConfig m, MonadIO (mt m), MonadIO m) => mt m () mainProgram = do input <- getUserInput lift $ doLog input liftIO $ putStrLn $ "Entry logged: " ++ input
However, this has the unfortunate effect that the function is no longer completely general, because the order in which two readers appear on the monad stack is blocked. There may be a cleaner way to achieve this, but I could not figure it out of my head without sacrificing (even more) the pedigree.
shang Oct 22 2018-12-12T00: 00Z
source share