为什么没有 ExceptT 的 MonadMask 实例?
Why is there no MonadMask instance for ExceptT?
Edward Kmett 的 exceptions 库不提供 MonadMask instance for 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.
但它的意义对我来说不是不言而喻的。 "multiple exit" 是什么意思,为什么它禁止 MonadMask
实例?
[...] '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)
我写的这个实例是不是不能正常运行?
A class for monads which provide for the ability to account for all possible exit points from a computation, and to mask asynchronous exceptions. Continuation-based monads, and stacks such as ErrorT e IO
which provide for multiple failure modes, are invalid instances of this class.
当您将 ErrorT
/ExceptT
与 IO
一起使用时,具有 "multiple exit points" 指的是您可以拥有运行时异常或在单子。其中任何一个都会结束计算。
runExceptT $ do
error "This is an exit point."
throwError "This is another exit point."
return 23
可以为 ExceptT
编写一个 MonadMask
,它对所有 ExceptT e m a
都有效,前提是基础 monad m
是 不是 IO。因此,关于将 CatchT
与 IO
一起使用的巨大警告(这样做会使 MonadMask
实例无效)。
下面的程序演示了您的实例的问题:您可以使用 Left
提前退出,从而导致终结器永远不会成为 运行。这与 MonadMask
文档中规定的法律相反,后者要求 f `finally` g
g
被执行,而不管 f
中发生了什么。终结器永远不会 运行 的原因非常简单:如果没有抛出异常 finally
(或 bracket
这是 finally
的实现方式)只需使用 >>=
到 运行 之后的终结器但是 >>=
如果左 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"))
自从 exceptions v0.9.0 已于 2018 年 2 月 25 日上传至 hackage 以来,这似乎不再正确。
P.S。 0.9.0被认为是废弃的,0.10.0被推荐使用(参见http://hackage.haskell.org/package/exceptions-0.10.0/changelog)。
Edward Kmett 的 exceptions 库不提供 MonadMask instance for ExceptT.
Ben Gamari once asked about this然后得出结论是文档说明了。这是我能找到的最接近相关的段落:
Note that this package does provide a
MonadMask
instance forCatchT
. This instance is only valid if the base monad provides no ability to provide multiple exit. For example,IO
orEither
would be invalid base monads, butReader
orState
would be acceptable.
但它的意义对我来说不是不言而喻的。 "multiple exit" 是什么意思,为什么它禁止 MonadMask
实例?
[...] '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
overIO
.
也许问这个替代问题会更清楚:如果我们搁置变压器并考虑稍微简单的类型:
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)
我写的这个实例是不是不能正常运行?
A class for monads which provide for the ability to account for all possible exit points from a computation, and to mask asynchronous exceptions. Continuation-based monads, and stacks such as
ErrorT e IO
which provide for multiple failure modes, are invalid instances of this class.
当您将 ErrorT
/ExceptT
与 IO
一起使用时,具有 "multiple exit points" 指的是您可以拥有运行时异常或在单子。其中任何一个都会结束计算。
runExceptT $ do
error "This is an exit point."
throwError "This is another exit point."
return 23
可以为 ExceptT
编写一个 MonadMask
,它对所有 ExceptT e m a
都有效,前提是基础 monad m
是 不是 IO。因此,关于将 CatchT
与 IO
一起使用的巨大警告(这样做会使 MonadMask
实例无效)。
下面的程序演示了您的实例的问题:您可以使用 Left
提前退出,从而导致终结器永远不会成为 运行。这与 MonadMask
文档中规定的法律相反,后者要求 f `finally` g
g
被执行,而不管 f
中发生了什么。终结器永远不会 运行 的原因非常简单:如果没有抛出异常 finally
(或 bracket
这是 finally
的实现方式)只需使用 >>=
到 运行 之后的终结器但是 >>=
如果左 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"))
自从 exceptions v0.9.0 已于 2018 年 2 月 25 日上传至 hackage 以来,这似乎不再正确。
P.S。 0.9.0被认为是废弃的,0.10.0被推荐使用(参见http://hackage.haskell.org/package/exceptions-0.10.0/changelog)。