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