Если f
обращается к облаку, то f
, несомненно, использует некоторую монаду, вероятно, монаду IO
или монаду, полученную из монады IO
. Существуют монадические версии map
. Вот что вы обычно делаете в качестве первой попытки:
f :: A -> IO B -- defined elsewhere
g :: [A] -> IO [B]
g xs = mapM f xs
-- or, in points-free style:
g = mapM f
Имеет (возможно) нежелательное свойство, что g
не будет выполнен, не возвращая значений, если какой-либо вызов f
не удастся. Мы исправим это, сделав так, чтобы f
возвращал либо ответ, либо сообщение об ошибке.
type Error = String
f :: A -> IO (Either Error B)
g :: [A] -> IO [Either Error B]
g = mapM f
Если вы хотите, чтобы все ошибки возвращались вместе, а все успехи объединялись вместе, вы можете использовать функции lefts
и rights
из Data.Either
.
h :: [A] -> IO ([B], [Error])
h xs = do ys <- g xs
return (rights ys, lefts ys)
Если вам не нужны сообщения об ошибках, просто используйте Maybe B
вместо Either Error B
.
Тип данных Either
является наиболее распространенным способом представления значения, которое может привести либо к ошибке, либо к правильному значению. Ошибки используют конструктор Left
, правильные значения - конструктор Right
. В качестве бонуса «право» также означает «правильный» на английском языке, но причина, по которой правильное значение использует конструктор Right
, на самом деле глубже (потому что это означает, что мы можем создать функтор типа Either, который изменяет правильные результаты что невозможно по конструктору Left
).