У меня есть некоторые функции, написанные на C, которые я вызываю из Haskell. Эти функции возвращают 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]
Что дает мне эту ошибку:
<interactive>:1:22: Couldn't match expected type `[b]' against inferred type `IO a' In the first argument of `mapM', namely `f' In the second argument of `($)', namely `mapM f ([0 .. 5])' In the expression: takeWhile (< 4) $ mapM f ([0 .. 5])
И это имеет смысл для меня - результат все еще содержится в монаде IO, и я не могу просто сравнить два значения, содержащиеся в монаде IO. Я знаю, что именно цель монадов - объединение результатов вместе и отбрасывание операций при выполнении определенного условия - но есть ли простой способ "обернуть" монаду IO в этом случае, чтобы остановить выполнение цепи при условии по моему выбору, без написания экземпляра MonadPlus
?
Могу ли я просто "разблокировать" значения из f
, для целей takeWhile?
Является ли это решением, когда функторы подходят? Функторы еще не "нажали" со мной, но у меня вроде бы создается впечатление, что это может быть хорошей ситуацией для их использования.
<ч/" > Update:
@sth имеет самый близкий ответ на то, что я хочу - на самом деле, это почти то, что я собирался, но мне все же хотелось бы посмотреть, есть ли стандартное решение, которое явно не рекурсивно - это Хаскелл, в конце концов! Оглядываясь назад, как я сформулировал свой вопрос, теперь я вижу, что я недостаточно ясно о желаемом поведении.
Функция 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 answer выполняет точное поведение, которое я хочу, за исключением того, что результаты могут (должны?) быть отброшены. Функция A dropWhileM_
была бы эквивалентна для моих целей. Почему в Control.Monad нет такой функции или takeWhileM_
? Я вижу, что было аналогичное обсуждение в списке рассылки, но, похоже, ничего из этого не произошло.