3

能够组合不同的 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

4

1 回答 1

1

您需要将它们分层成一个堆叠的 monad,但它们不能堆叠在一起,因为它们都声明这IO正是包装的 monad。幸运的是,您的代码已经足够通用,可以解除这个限制。最通用的函数类型使用MonadIO而不是专门使用IO. 如果您将类型更改为

 logit :: MonadIO m => Text -> ReaderT Bool m ()
 foo   :: MonadIO m =>         ReaderT Text m ()

然后liftIO调用会将IO整个堆栈中的动作提升IO到底部的 monad。

需要明确的是,您编写的类型不需要使用liftIO- just 可以满足相同的类型lift,但是因为IO(通常)是一个实例,MonadIO那么您的(过度)专业化类型也将通过检查器。

于 2013-01-30T16:58:45.210 回答