Ru/Monad

From HaskellWiki
< Ru

(перенесено из статьи введение в 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]

Ссылки