Chr*_*tin 14 haskell exception-handling monad-transformers
Edward Kmett的异常库不为ExceptT提供MonadMask实例.
Ben Gamari曾经问过这件事,然后得出结论,文件对此进行了解释.这是我能找到的最接近相关的段落:
请注意,此包确实提供了一个
MonadMask
实例CatchT
.此实例仅在基本monad无法提供多个退出时才有效.例如,IO
或者Either
是无效的基础monad,但是Reader
或者State
是可以接受的.
但它的含义对我来说并不是不言而喻的."多重退出"意味着什么,为什么禁止MonadMask
实例?
[...]'MonadMask',它允许您保证运行某些操作,即使存在异常(同步和异步).为了提供该保证,monad堆栈必须能够控制其执行流程.特别是,这排除了具有多个退出点的Monads的实例,例如
ErrorT
overIO
.
或许可以更清楚地提出这个替代问题:如果我们搁置变压器并考虑稍微简单的类型:
data IOEither a = IOEither { unIOEither :: IO (Either String a) }
deriving Functor
Run Code Online (Sandbox Code Playgroud)
这似乎是我们其实可以写一个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)
Run Code Online (Sandbox Code Playgroud)
这个实例我写的不正常吗?
下面是一个演示实例问题的程序:您可以提前退出Left
,从而导致终结器永远不会运行.这与文档中所述的法律相反,MonadMask
后者要求f `finally` g
g
执行for 而不管发生什么f
.终结器永远不会运行的原因很简单:如果没有抛出异常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"))
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
575 次 |
最近记录: |