Edward Kmett的例外库未为MonadMask提供ExceptT个实例。
Ben Gamari once asked about this然后得出结论,文件对此进行了解释。这是我能找到的最接近相关的段落:
请注意,此程序包确实为
MonadMask
提供了CatchT
个实例。如果基本monad不提供提供多个退出的功能,则此实例仅 有效。例如,IO
或Either
将是无效的基础monad,但Reader
或State
是可以接受的。
但它的含义对我来说并不是不言而喻的。 “多重退出”是什么意思,为什么它禁止MonadMask
实例?
[...]'MonadMask',它允许您保证运行某些操作,即使存在异常(同步和异步)。为了提供该保证,monad堆栈必须能够控制其执行流程。特别是,这排除了具有多个退出点的Monads的实例,例如
ErrorT
超过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)
我写的这个实例不能正常工作吗?
答案 0 :(得分:7)
下面是一个演示实例问题的程序:您可以使用Left
提前退出,从而导致终结器永远不会运行。这与MonadMask
文档中规定的法律形成对比,后者要求f `finally` g
执行g
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"))
不会执行正确的参数。
std::string TimeStamp (const time_t seconds) // version-1
{
auto tm = *std::localtime(&seconds); // <ctime>
char readable[30] = {};
std::strftime(&readable[0], sizeof(readable) - 1, "%Y-%h-%d %H:%M:%S:", &tm);
return readable;
}
答案 1 :(得分:2)
monad的类,它提供了计算所有可能的退出点的能力,并掩盖了异步异常。基于连续的monad和诸如
ErrorT e IO
的堆栈(提供多种故障模式)是此类的无效实例。
当ErrorT
/ ExceptT
与IO
一起使用时,有多个退出点&#34;指的是您可以在Monad中引发运行时异常或异常。其中任何一个都会结束计算。
runExceptT $ do
error "This is an exit point."
throwError "This is another exit point."
return 23
可以为MonadMask
写一个对ExceptT
有效的ExceptT e m a
,前提是基础monad m
不< / em> IO。因此,关于将CatchT
与IO
一起使用的大量警告(这样做会使MonadMask
实例无效)。
答案 2 :(得分:0)
自exceptions v0.9.0被上传到黑客 2018年2月25日以来,这似乎不再正确。
P.S。 0.9.0 被认为已弃用,建议使用 0.10.0 (请参见http://hackage.haskell.org/package/exceptions-0.10.0/changelog)。