为什么ExceptT没有MonadMask实例? [英] Why is there no MonadMask instance for ExceptT?

查看:184
本文介绍了为什么ExceptT没有MonadMask实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Edward Kmett的例外库没有提供 MonadMask 实例 ExceptT

Edward Kmett's exceptions library does not provide a MonadMask instance for ExceptT.

Ben Gamari曾经问过这个问题,然后得出结论说这是由文档解释的。这是我能找到的最相关的相关段落:

Ben Gamari once asked about this and then concluded that it was explained by the documentation. This is the closest relevant-looking passage I can find:


请注意,此包确实提供了 MonadMask

Note that this package does provide a MonadMask instance for CatchT. This instance is only valid if the base monad provides no ability to provide multiple exit. For example, IO or Either would be invalid base monads, but Reader or State would be acceptable.

但其含义不是对我来说不言而喻。 多次退出是什么意思,为什么它禁止 MonadMask 实例?

But its meaning is not self-evident to me. What does "multiple exit" mean and why does it prohibit a MonadMask instance?

Michael Snoyman也写道:

Michael Snoyman also writes:


[MonoMask],它允许您保证即使在出现异常(同步和异步)的情况下也能运行某些操作。为了提供这种保证,monad堆栈必须能够控制其执行流程。尤其是,这不包括具有多个出口点的Monads实例,如 ErrorT over IO

也许更清楚地问一下这个替代问题:如果我们放置变压器并考虑稍简单的类型:

Perhaps it would be more clear to ask this alternative question: If we set aside transformers and consider the slightly simpler type:

data IOEither a = IOEither { unIOEither :: IO (Either String a) }
    deriving Functor

它似乎我们实际上可以写一个 MonadMask instance:

It seems that we can in fact write a MonadMask instance:

instance Applicative IOEither where
    pure = IOEither . return . Right
    IOEither fIO <*> IOEither xIO = IOEither $
        fIO >>= either (return . Left) (\f -> (fmap . fmap) f xIO)

instance Monad IOEither where
    IOEither xIO >>= f = IOEither $
        xIO >>= either (return . Left) (\x -> unIOEither (f x))

instance MonadThrow IOEither where
    throwM e = IOEither (throwM @IO e)

instance MonadCatch IOEither where
    catch (IOEither aIO) f = IOEither $ catch @IO aIO (unIOEither . f)

instance MonadMask IOEither where
    mask f = IOEither $ mask @IO $ \restore ->
        unIOEither $ f (IOEither . restore . unIOEither)
    uninterruptibleMask f = IOEither $ uninterruptibleMask @IO $ \restore ->
        unIOEither $ f (IOEither . restore . unIOEither)

我写的这个实例不正常工作?

Does this instance I've written not work properly?

推荐答案

下面是一个程序,演示您的实例的问题:您可以早点退出 Left ,从而导致终结器永远不会运行。这与文档中为 MonadMask 所述的法律形成鲜明对比,这些法律要求 f`finally`g <$不管在 f 中发生了什么,都会执行c $ c> g 。终结者永远不会运行的原因很简单:如果没有异常被抛出 finally (或者括号这就是 finally 实现)只是使用>> = 来运行终结器,但是> ;> = 如果左侧返回 Left ,则不执行正确的参数。

Below is a program that demonstrates the problem with your instances: You can exit early with Left and thereby cause the finalizer to never be run. This is in contrast to the law stated in the docs for MonadMask which require that for f `finally` g g is executed regardless of what happens in f. The reason why the finalizer is never run is quite simple: If no exception is thrown finally (or bracket which is how finally is implemented) just uses >>= to run the finalizer afterwards but >>= does not execute the right argument if the left returns Left.

data IOEither a = IOEither { unIOEither :: IO (Either String a) }
    deriving Functor

instance Applicative IOEither where
    pure = IOEither . return . Right
    IOEither fIO <*> IOEither xIO = IOEither $
        fIO >>= either (return . Left) (\f -> (fmap . fmap) f xIO)

instance Monad IOEither where
    IOEither xIO >>= f = IOEither $
        xIO >>= either (return . Left) (\x -> unIOEither (f x))

instance MonadThrow IOEither where
    throwM e = IOEither (throwM @IO e)

instance MonadCatch IOEither where
    catch (IOEither aIO) f = IOEither $ catch @IO aIO (unIOEither . f)

instance MonadMask IOEither where
    mask f = IOEither $ mask @IO $ \restore ->
        unIOEither $ f (IOEither . restore . unIOEither)
    uninterruptibleMask f = IOEither $ uninterruptibleMask @IO $ \restore ->
        unIOEither $ f (IOEither . restore . unIOEither)

instance MonadIO IOEither where
  liftIO x = IOEither (Right <$> x)

main :: IO ()
main = void $ unIOEither $ finally (IOEither (return (Left "exit")))
                                   (liftIO (putStrLn "finalizer"))

这篇关于为什么ExceptT没有MonadMask实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆