This is the problem of bonding monads. Not in the form of a stack, but in the form of the need to deploy one monad to start an operation inside another.
Two domains: Weblog and application. But keep in mind that the application domain will call additional, just as it is currently calling in the Weblog. Both have their own monad stacks. Both keep track of their condition.
newtype WeblogM a = WeblogM (ReaderT Weblog (ErrorT WeblogError IO) a) deriving (Monad, MonadIO, Reader.MonadReader Weblog, Error.MonadError WeblogError) newtype AppM a = AppM (ReaderT App (EitherT AppError IO) a) deriving ( Functor, Applicative, Monad , MonadReader App, MonadError AppError)
To start the WeblogM operation inside the AppM function, I found that I need to deploy WeblogM and overwrite it using the following functions:
runWeblogHere :: forall a. Weblog.Weblog -> Weblog.WeblogM a -> AppM a runWeblogHere weblog action = runIO (left . WeblogError) (Weblog.runWeblog weblog action) runIO :: (e -> EitherT AppError IO a) -> IO (Either ea) -> AppM a runIO handler = AppM . lift . handleT handler . EitherT
However, this really makes my actual pass-through operations pretty simple:
getPage :: Weblog.PageId -> AppM Weblog.WikiPage getPage pageid = do App{weblog} <- ask runWeblogHere weblog $ Weblog.getWikiPage pageid
This bothers me already because I have other monadic libraries that I already know that I'm going to connect to the AppM architecture, and I'm worried about writing the runXHere method, which is really a template, for each of them.
I have a suggestion to create a MonadWeblog class to match WeblogM , in much the same way that MonadReader matches ReaderT . I like this more because I can start isolating the monad cache in my MonadWeblog instance (or, indeed, MonadX ).
haskell monads monad-transformers
Savanni D'Gerinel
source share