It seems that it would be useful to be able to combine different ReaderT environments.
For instance, a generic logging facility might look something like this:
logit :: Text -> ReaderT Bool IO ()
logit str = do debugflag <- ask
liftIO $ if debugflag then putStrLn ("debug: " ++ str) else return ()
This looks like a nice reusable component. So how would I go about integrating this definition with another ReaderT environment so that I could use both of them?
For instance, suppose I want to combine it with this ReaderT instance:
foo :: ReaderT Text IO ()
foo = ...
so that I can use both foo
and logit
in the same function.
You'll want to layer them into a stacked monad, but they can't be stacked together since both of them declare that IO
is exactly the wrapped monad. Fortunately, your code is already general enough to lift this restriction. The most general types of your functions use MonadIO
instead of specifically using IO
. If you change the types to
logit :: MonadIO m => Text -> ReaderT Bool m ()
foo :: MonadIO m => ReaderT Text m ()
then the liftIO
call will lift the IO
actions through the entire stack to an IO
monad at the bottom.
To be clear, the types you've written do not need to use liftIO
—the same type would be satisfied by just lift
, but since IO
is (trivially) an instance of MonadIO
then your (overly) specialized type will also pass the checker.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.