是否有可能在出现异常的情况下可以共享一部分收集的tell
的WriterT monad?如果我在try
之外使用runWriterT
,则w
似乎已被丢弃。如果我尝试在内部添加try
,则似乎需要MonadUnliftIO
。 MonadUnliftIO
听起来可能对我有帮助,但是该软件包说它只能解除单子(monad)语境,而不能解除我想为Writer的单子(monad)态。有没有人用Writer或类似工具做到这一点?
伪代码示例:
x <- runWriterT $ do
result <- try $ do
tell "a"
tell "b"
error "c"
tell "d"
case result of
Left e -> Just e
Right a -> Nothing
x `shouldBe` (Just "c", "ab")
最佳答案
好吧,您的代码使用了error
。从道德上讲,所有押注都用error
进行,因为它比其他任何事物都更能说明程序中的错误。 IO
可以捕获由它产生的异常这一事实实际上只是一个有趣的怪癖。因此,如果您需要这种行为,那么最好使用适当的异常monad转换器,例如@ Li-yaoXia建议。
-- see Control.Monad.Except
action :: (MonadExcept String m, MonadWriter String m) =>
m ()
action = do tell "a"
tell "b"
throwError "c"
tell "d"
-- run action and massage it into your format
yourOutput :: (Maybe String, String)
yourOutput = runWriter $ fmap (either Just (const Nothing)) $ runExceptT actions
至于为什么
error
不能真正起作用(至少以一种不错的方式),请考虑error _ :: WriterT w m a
的实际含义。 error _ :: Int
的意思是“这里应该有一个数字,但是只有一个错误。” WriterT w m a
是一种程序;保留类型为w
的日志,执行其他操作(m
)并返回a
的程序的类型。因此,error _ :: WriterT w m a
并不意味着“抛出可恢复错误的程序,保留了w
类型的日志,”它的意思是“这里应该有一个程序,但是只有一个错误。”隐喻地说,您发布的action
伪代码突然用完了程序,即使该类型没有提及您的程序被允许突然终止,您也应该(隐喻地)感谢幸运星允许您进行设置替换程序(使用try
),而不是由于错误而受到适当的惩罚!假设象牙顶的讲道已被阻止,让我们假设我们确实有
action :: MonadWriter String m => m ()
action = do tell "a"
tell "b"
error "c"
tell "d"
我们只需要处理它。假设您使用的是
Writer
的惰性版本,那么您会很高兴地注意到runWriter action =
( ()
, "a" ++ "b" ++ (case error "c" of (_, c) -> c) ++ "d"
)
如果存在在评估书脊时发生的情况,则存在通过捕获不纯的异常(不道德的“我实际上说的没有程序”这种类型的
error
)来“挽救”列表的功能。-- can be recast as Free (a,) () -> IO (Free (a,) (Maybe e))
-- essentially, that type encodes the intuition that a list may end in [] (nil)
-- or in an error
salvageList :: Exception e => [a] -> IO ([a], Maybe e)
salvageList xs = catch (do xs' <- evaluate xs
case xs' of
[] -> return ([], Nothing)
(x : tl) -> do (tl', e) <- salvageList tl
return (x : tl', e)
) (\e -> return ([], Just e))
哪个有效:
-- we get the return value, too! that makes me feel... surprisingly weirded out!
yourOutputPlus :: IO ((), Maybe String, String)
yourOutputPlus = do let (val, log) = runWriter action
(realLog, error) <- salvageList log
return (val, fmap (\(ErrorCall msg) -> msg) error, realLog)
关于haskell - 如果发生异常,是否可以访问 `WriterT`的部分收集的 `tell`?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55786595/