haskell - 如何终止在 `IO` monad 中运行的计算?

标签 haskell exception io monads short-circuiting

有一个库提供数据类型F和类型的函数

ffoldlIO :: (b -> a -> IO b) -> b -> F a -> IO b

功能类似

foldlIO :: (b -> a -> IO b) -> b -> [a] -> IO b
foldlIO f a = \xs -> foldr (\x r (!a') -> f a' x >>= r) return xs a

我想知道 foldlIO(以及 ffoldlIO)是否可以以短路方式运行。

考虑这个例子:

example1 :: IO Int
example1 = foldlIO (\a x -> if a < 4 then return (a + x) else return a) 0 [1..5]

这里foldlIO遍历了整个列表,但是如果我们抛出一个异常来停止计算然后捕获它怎么办?像这样的事情:

data Terminate = Terminate
  deriving (Show)

instance Exception Terminate

example2 :: IO Int
example2 = do
  ra <- newIORef 0
  let step a x
        | a' < 4    = return a'
        | otherwise = writeIORef ra a' >> throwIO Terminate
        where a' = a + x
  foldlIO step 0 [1..] `catch` \(_ :: Terminate) -> readIORef ra

这可靠吗?有没有更好的方法来终止在 IO monad(而不是其他 monad)中运行的计算,或者我根本不应该这样做?

最佳答案

例如,您可以像这样使用 ContT monad 转换器:

example3 :: IO Int
example3 = flip runContT return . callCC $ \exit -> do
    let step a x
            | a' < 4    = return a'
            | otherwise = exit a'
            where a' = a + x
    foldM step 0 [1..]

此外,您还可以定义自己的具有终止可能性的 foldM 版本。

termFoldM :: (Monad m, Foldable t) =>
    ((b -> ContT b m c) -> b -> a -> ContT b m b) -> b -> t a -> m b
termFoldM f a t = flip runContT return . callCC $ \exit -> foldM (f exit) a xs

example4 :: IO Int
example4 = termFoldM step 0 [1..]
  where
    step exit a x
        | a' < 4    = return a'
        | otherwise = exit a'
        where a' = a + x

但是这种方式(使用ContT)有一个问题。您无法轻松执行某些 IO 操作。例如,此代码将不会被编译,因为 step 函数必须返回 ContT Int IO Int 类型的值,而不是 IO Int

let step a x
        | a' < 4    = putStrLn ("'a = " ++ show a') >> return a'
        | otherwise = exit a'
        where a' = a + x

幸运的是,您可以通过 lift 函数解决这个问题,如下所示:

let step a x
        | a' < 4    = lift (putStrLn ("'a = " ++ show a')) >> return a'
        | otherwise = exit a'
        where a' = a + x

关于haskell - 如何终止在 `IO` monad 中运行的计算?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41122812/

相关文章:

Haskell 位域和位级协议(protocol)

c# - 是否有异常处理标准?如果是,请提供链接

java - 是否应该允许 GET 请求触发异常?

c# - 使用 'try-finally' block 而不使用 'catch' block

node.js - 如何使用 Node.js 读取大文件的一部分?

file - 如何使用 RPGLE 在显示屏上显示消息文件中的消息?

haskell - 从解析器组合器序列构造长度为n的元组

Haskell Pipes——让管道消耗它产生的东西(本身)

haskell - 无法破坏传递类型

c - 这个 C 程序究竟是如何从这个二进制文件中读取数据的?