似乎能够组合不同的ReaderT环境会很有用。
例如,通用日志记录工具可能如下所示:
logit :: Text -> ReaderT Bool IO ()
logit str = do debugflag <- ask
liftIO $ if debugflag then putStrLn ("debug: " ++ str) else return ()
这看起来像一个不错的可重用组件。那么我该如何将这个定义与另一个ReaderT环境集成,以便我可以同时使用它们呢?
例如,假设我想将它与此ReaderT实例结合使用:
foo :: ReaderT Text IO ()
foo = ...
这样我就可以在同一个函数中同时使用foo
和logit
。
答案 0 :(得分:1)
你会想要将它们分层堆叠成monad,但它们不能堆叠在一起,因为它们都声明IO
正好是包装的monad。幸运的是,您的代码已足以解除此限制。最常见的功能类型使用MonadIO
,而不是专门使用IO
。如果您将类型更改为
logit :: MonadIO m => Text -> ReaderT Bool m ()
foo :: MonadIO m => ReaderT Text m ()
然后liftIO
调用会将整个堆栈中的IO
操作提升到底部的IO
monad。
要明确的是,您编写的类型不需要使用liftIO
- 只需lift
即可满足相同类型,但由于IO
是(平凡) MonadIO
的实例,那么你的(过度)专业类型也将通过检查器。