Ru/Monad
(перенесено из статьи введение в Haskell IO)
Немного глубже в do
Поначалу кажется, что do - какая-то чёрная магия, или встроенный синтаксис для грязных процедур. На самом деле всё это просто cинтаксический сахар, без которого можно обойтись, но который позволяет склеивать процедуры и делает их написание проще.
Для одного выражения do излишне:
main = do putStr "Привет!"
без do можно записать как:
main = putStr "Привет!"
Как же обойтись без do для нескольких действий?
main = do putStr "Как Вас зовут?"
putStr "Сколько Вам лет?"
putStr "Неплохой денёк сегодня!"
Тут do говорит, что действия идут одно за другим. Это переводится в оператор последовательности (>>):
main = (putStr "Как Вас зовут?") >> (putStr "Сколько Вам лет?") >> (putStr "Неплохой денёк сегодня!")
Более сложными являются примеры с (<-):
main = do a <- readLn
print a
Такой код переводится в:
main = readLn >>= (\a -> print a)
Выражения типа (x >>= (\y -> z)) означает "сделать x, взять его результат, подставить его в лямбду вместо y, вычислить z, и сделать z".
С этим оператором можно писать, к примеру, и так:
main = readLn >>= print
Чтобы закрепить принцип избавления от do, рассмотрим внимательно пример программы в трёх вариантах. Сначала идёт do, потом его перевод со скобками, и под конец - без них, что дозволительно благодаря приоритетам операторов:
main = main3
main1 = do putStr "Как Вас зовут? "
i <- readLn
putStr "Сколько Вам лет? "
v <- readLn
putStr "Привет, "
putStr i
putStr "! Неплохой денёк сегодня!"
main2 = (putStr "Как Вас зовут? ") >>
readLn >>= (
\i -> (
(putStr "Сколько Вам лет? ") >>
readLn >>= (
\v -> (
(putStr "Привет, ") >>
(putStr i) >>
(putStr "! Неплохой денёк сегодня!")
)
)
)
)
main3 = putStr "Как Вас зовут? " >>
readLn >>= \i ->
putStr "Сколько Вам лет? " >>
readLn >>= \v ->
putStr "Привет, " >>
putStr i >>
putStr "! Неплохой денёк сегодня!"
Зачем это всё знать? А все дело в том, что все эти операторы (и do) используются не только в процедурах, а и, к примеру, в списках. Их смысл можно даже переопределять!
Подробнее о списках:
a1 = do x <- [10,100,1000] -- перебрать все эти числа как x
y <- [1,2,3] -- перебрать 1..3 как y
return (x*y) -- слить (x*y) в список-результат
-- Результат: [10,20,30,100,200,300,1000,2000,3000]