Поведение карты и карты
Я нахожусь в главе I/O Real World Haskell. Монады не обсуждаются в книге еще в 7 главах. То есть мое понимание ввода-вывода в лучшем случае неполное.
Сейчас я пытаюсь понять функцию mapM. Как я понимаю, функция "выполняет" каждый элемент в списке, который должен быть "действием" (IO monad).
Что не имеет смысла, это этот пример. Почему mapM возвращает другой результат, чем карта для тех же аргументов?
Prelude> map (\x -> [x]) [0, 1, 2]
[[0],[1],[2]]
Prelude> mapM (\x -> [x]) [0, 1, 2]
[[0,1,2]]
Ответы
Ответ 1
Как я понимаю, функция "выполняет" каждый элемент в списке, который должен быть "действием" (IO monad).
Это верно для IO, но в вашем примере кода вы не используете монаду IO, вы используете монаду списка (функция, которую вы даете mapM, возвращает список ([x]
), а не IO).
mapM
определяется как mapM f as = sequence (map f as)
. Если f возвращает IO, это означает, что для каждого элемента в списке он строит IO, применяя f к элементу. Затем он превращает список IO, который возвращает карту в IO "содержащий" список с использованием последовательности (поэтому, когда вы выполняете IO, вы возвращаете список, содержащий значения, отличные от IO).
Для списков это означает, что он создает список списков, применяя f
к каждому элементу as
. Затем он использует sequence
для создания списка списков, который содержит все возможные способы взять один элемент из каждого списка в списках списков (например, sequence [[1,2],[3,4]]
возвращает [[1,3],[1,4],[2,3],[2,4]]
).
Ответ 2
Возможно, стоит отметить, что эти два фрагмента не являются "аналогичными", и вы не должны ожидать связанных результатов. В частности, "монадическая" версия
map (\ x → [x]) [0, 1, 2]
является
mapM (\ x → return [x]) [0, 1, 2]
Обратите внимание на дополнительный return
.
В общем случае return (map f x)
совпадает с mapM (return . f) x
.
Это потому, что для монады списка x >>= f
"flattens" результат применения f
к x
. Когда вы оставили значение return
, результаты применения \x -> [x]
были сплющены в результате. Наличие дополнительного return
отменяет дополнительное выравнивание.