У меня есть некоторые функции, написанные на 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_? Я вижу, что было аналогичное обсуждение в списке рассылки, но, похоже, ничего из этого не произошло.