У меня есть некоторые функции, написанные на 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])
И это имеет смысл для меня - результат все еще содержится в монаде ввода-вывода, и я не могу просто сравнить два значения, содержащиеся в монаде ввода-вывода. Я знаю, что именно в этом и заключается цель монад - объединение результатов воедино и отбрасывание операций при выполнении определенного условия - но есть ли в этом случае простой способ «обернуть» монаду ввода-вывода, чтобы прекратить выполнение цепочки при условии по моему выбору, без написания экземпляра MonadPlus
?
Могу ли я просто "снять" значения с f
для целей takeWhile?
Это решение для функторов? Функторы еще не «нажали» на меня, но у меня сложилось впечатление, что это может быть хорошая ситуация для их использования.
<ч />
Обновление:
@ sth имеет самый близкий ответ на то, что я хочу - на самом деле, это почти то, к чему я стремился, но я все еще хотел бы посмотреть, есть ли стандартное решение , которое не соответствует явно рекурсивный - это ведь Haskell! Оглядываясь назад на то, как я сформулировал свой вопрос, теперь я понимаю, что не совсем ясно о желаемом поведении.
Функция f
, которую я использовал выше для примера, была просто примером. Реальные функции написаны на C и используются исключительно для их побочных эффектов. Я не могу использовать предложение Тома о 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_
? Я вижу, что было подобное обсуждение в списке рассылки , но, похоже, ничего не вышло.