我有一些从 Haskell 调用的用 C 语言编写的函数。这些函数返回IO (CInt)
. 有时我想运行所有函数,而不管它们返回什么,这很容易。为了示例代码,这是当前正在发生的事情的一般概念:
Prelude> let f x = print x >> return x
Prelude> mapM_ f [0..5]
0
1
2
3
4
5
Prelude>
我得到了我想要的副作用,我不在乎结果。但是现在我需要在第一个没有返回我想要的结果的项目之后立即停止执行。假设返回值 4 或更高需要停止执行 - 那么我想要做的是:
Prelude> takeWhile (<4) $ mapM f [0..5]
这给了我这个错误:
<交互式>:1:22: 无法将预期类型“[b]”与推断类型“IO a”匹配 在 `mapM' 的第一个参数中,即 `f' 在`($)'的第二个参数中,即`mapM f([0 .. 5])' 在表达式中:takeWhile (< 4) $ mapM f ([0 .. 5])
这对我来说很有意义——结果仍然包含在 IO monad 中,我不能只比较 IO monad 中包含的两个值。我知道这正是 monad 的目的——将结果链接在一起并在满足某个条件时丢弃操作——但是在这种情况下是否有一种简单的方法可以“包装” IO monad 以在某个条件下停止执行链我选择的,没有写一个实例MonadPlus
?
为了 takeWhile 的目的,我可以从f
中“取消”这些值吗?
这是适合函子的解决方案吗?Functors 还没有和我一起“点击”,但我有一种印象,这可能是使用它们的好情况。
更新:
@sth 有我想要的最接近的答案 - 事实上,这几乎正是我想要的,但我仍然想看看是否有一个标准解决方案不是明确递归的 - 这是 Haskell,之后全部!回顾我如何表达我的问题,现在我可以看到我对我想要的行为不够清楚。
f
我上面用于示例的函数只是一个示例。真正的函数是用 C 语言编写的,专门用于它们的副作用。我不能使用@Tom 的建议,mapM_ f (takeWhile (<4) [0..5])
因为在执行之前我不知道任何输入是否真的会导致成功或失败。
我实际上也不关心返回的列表——我只想调用 C 函数,直到列表用尽或第一个 C 函数返回失败代码。
在 C 风格的伪代码中,我的行为是:
do {
result = function_with_side_effects(input_list[index++]);
} while (result == success && index < max_index);
同样,@sth 的答案执行了我想要的确切行为,除了结果可能(应该?)被丢弃。一个dropWhileM_
函数对于我的目的来说是等价的。takeWhileM_
为什么在 Control.Monad中没有这样的函数?我看到在邮件列表上有类似的讨论,但似乎没有任何结果。