och*_*les 12 haskell monad-transformers
我目前正在构建一个新的API,它目前提供的功能之一是:
inSpan :: Tracer -> Text -> IO a -> IO a
Run Code Online (Sandbox Code Playgroud)
我Tracer想把它变成一个monad,给我一个更像的签名
inSpan :: MonadTracer m => Text -> m a -> m a
Run Code Online (Sandbox Code Playgroud)
实行inSpan用途bracket,这意味着我有两个主要选择:
class MonadUnliftIO m => MonadTracer m
Run Code Online (Sandbox Code Playgroud)
要么
class MonadMask m => MonadTracer m
Run Code Online (Sandbox Code Playgroud)
但我应该选择哪个?请注意,我控制了我提到的所有类型,这使我略微倾向于MonadMask因为它不在IO底层强制执行(也就是说,我们可能有一个纯MonadTracer实例).
还有什么我应该考虑的吗?
Mic*_*man 27
让我们首先列出选项(在此过程中重复您的一些问题):
MonadMask来自exceptions图书馆.这可以适用于各种单子和变换器,并且不需要基本monad IO.MonadUnliftIO来自unliftio-core(或unliftio)库.这个库只适用于IO在它们的基础上的monad ,并且它在某种程度上是同构的ReaderT env IO.MonadBaseControl来自monad-control图书馆.该库需要IO在基础上,但允许非ReaderT.现在做出权衡.MonadUnliftIO是该战斗的最新成员,并且拥有最不发达的图书馆支持.这意味着,除了monads可以作为实例的限制之外,许多好的实例还没有被编写.
重要的问题是:为什么MonadUnliftIO围绕ReaderT类似事物制造这种看似随意的要求?这是为了防止失去一元状态的问题.例如,语义bracket_ (put 1) (put 2) (put 3)不是很清楚,因此MonadUnliftIO不允许StateT实例.
MonadBaseControl放宽ReaderT限制,并提供更广泛的图书馆支持.它内部也被认为比其他两个更复杂,但对于你的用法而言并不重要.如上所述,它允许你在monadic状态下犯错误.如果你在使用中小心,这没关系.
MonadMask允许完全纯粹的变压器堆栈.我认为围绕在纯栈中建模异步异常的有用性有一个很好的论据,但我理解这种方法是人们有时想要做的事情.作为获得更多实例的交换,你仍然有monadic状态的限制,加上无法取消一些IO控制动作,比如timeout或forkIO.
我的建议:
MonadMask,这是最好的解决方案.timeout或withMVar什么的,使用MonadBaseControl.MonadUnliftIO.