【发布时间】:2009-07-15 20:27:39
【问题描述】:
我有一些从 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 (这对我来说很有意义 - 结果仍然包含在 IO monad 中,我不能只比较 IO monad 中包含的两个值。我知道这正是 monad 的目的——将结果链接在一起并在满足某个条件时丢弃操作——但是在这种情况下是否有一种简单的方法可以“包装” IO monad 以在某个条件下停止执行链我选择的,没有写MonadPlus 的实例?
为了 takeWhile 的目的,我可以从f 中“取消”这些值吗?
这是适合函子的解决方案吗? Functors 还没有和我一起“点击”,但我有一种印象,这可能是使用它们的好时机。
更新:
@sth 有最接近我想要的答案 - 事实上,这几乎正是我想要的,但我仍然想看看是否有一个 standard 解决方案不是' t 显式递归——毕竟这是 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_ 函数对于我的目的是等效的。为什么 Control.Monad 中没有像 takeWhileM_ 这样的函数?我看到有a similar discussion on a mailing list,但似乎什么都没有发生。
【问题讨论】:
-
sortBy没有按照sortByM :: Monad m => (a -> a -> m Ordering) -> [a] -> m [a]定义也有点失望。