Haskell 对于类似括号的函数,我应该更喜欢Monadenliftio还是Monademask?
我目前正在构建一个新的API,它目前提供的功能之一是:Haskell 对于类似括号的函数,我应该更喜欢Monadenliftio还是Monademask?,haskell,monad-transformers,Haskell,Monad Transformers,我目前正在构建一个新的API,它目前提供的功能之一是: inSpan :: Tracer -> Text -> IO a -> IO a 我希望将跟踪器移动到单子中,给我一个更像 inSpan :: MonadTracer m => Text -> m a -> m a inSpan的实现使用了括号,这意味着我有两个主要选项: class MonadUnliftIO m => MonadTracer m 或 但是我更喜欢哪一个呢?请注意,我控制着
inSpan :: Tracer -> Text -> IO a -> IO a
我希望将跟踪器
移动到单子中,给我一个更像
inSpan :: MonadTracer m => Text -> m a -> m a
inSpan
的实现使用了括号
,这意味着我有两个主要选项:
class MonadUnliftIO m => MonadTracer m
或
但是我更喜欢哪一个呢?请注意,我控制着我提到的所有类型,这使我稍微倾向于MonadMask
,因为它不会在底部强制执行IO
(也就是说,我们可能有一个纯MonadTracer
实例)
<> P>我们还有什么要考虑的吗? < P>让我们先列出选项(在过程中重复一些问题):
库中的异常
。这可以在广泛的monad和transformer上工作,并且不需要基本monad是MonadMask
IO
(或unliftio
)库中的unliftio
。此库仅适用于底部带有MonadUnliftIO
的单子,并且在某种程度上与IO
同构ReaderT env IO
- 来自
monad控件库的
。此库需要基本的MonadBaseControl
,但允许非ReaderTIO
Monaduliftio
是最新加入的竞争对手,拥有最不发达的库支持。这意味着,除了monad可以作为实例的限制之外,许多好的实例还没有被编写出来
重要的问题是:monaduliftio
为什么对ReaderT
这样的东西提出这种看似武断的要求?这是为了防止失去一元状态的问题。例如,方括号(put 1)(put 2)(put 3)
的语义并不十分清楚,因此monadulliftio
不允许使用StateT
实例
MonadBaseControl
放松了对ReaderT
的限制,并具有更广泛的库支持。它在内部也被认为比其他两个更复杂,但是对于你的用法来说,这并不重要。它允许你犯上述一元状态的错误。如果你小心使用,这没关系
MonadMask
允许完全纯变压器堆栈。我认为围绕在纯堆栈中建模异步异常的有用性有一个很好的论据,但我知道这种方法有时是人们想要做的。作为获得更多实例的交换,您仍然有关于一元状态的限制,加上无法解除某些IO
控制操作,如timeout
或forkIO
我的建议是:
- 如果您想与当今大多数人的做事方式相匹配,最好选择
,这是最常用的解决方案MonadMask
- 如果您想要实现该目标,但还需要使用MVAR执行
或timeout
操作,请使用
MonadBaseControl
- 如果您知道有一组特定的monad需要与之兼容,并且希望在编译时保证代码相对于monadic状态的正确性,请使用
monadnliftio
class MonadMask m => MonadTracer m