gpt4 book ai didi

haskell - 为什么 exceptT 没有 MonadMask 实例?

转载 作者:行者123 更新时间:2023-12-02 17:50:33 25 4
gpt4 key购买 nike

Edward Kmett 的异常库不提供MonadMask ExceptT 的实例.

Ben Gamari once asked about this然后得出结论,文档对此进行了解释。这是我能找到的最接近相关的段落:

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 实例?

Michael Snoyman also writes :

[...] 'MonadMask', which allows you to guarantee that certain actions are run, even in the presence of exceptions (both synchronous and asynchronous). In order to provide that guarantee, the monad stack must be able to control its flow of execution. In particular, this excludes instances for [...] Monads with multiple exit points, such as ErrorT over IO.

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

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

看起来我们实际上可以编写一个 MonadMask 实例:

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)

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

最佳答案

下面的程序演示了您的实例的问题:您可以使用 Left 提前退出,从而导致终结器永远不会运行。这与 MonadMask 文档中规定的法律相反,该法律要求无论发生什么情况,都执行 f `finally` g gf中。终结器永远不会运行的原因非常简单:如果没有抛出异常 finally (或 bracket 这是 finally 的实现方式)之后使用 >>= 运行终结器,但如果左侧返回 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"))

关于haskell - 为什么 exceptT 没有 MonadMask 实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41966893/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com