Изучай Haskell во имя добра! (Липовача) - страница 241

Левое тождество и правое тождество являются, по сути, законами, которые описывают, как должна вести себя функция >return. Это важная функция для превращения обычных значений в монадические, и было бы нехорошо, если бы монадическое значение, которое она произвела, имело больше, чем необходимый минимальный контекст.

Ассоциативность

Последний монадический закон говорит, что когда у нас есть цепочка применений монадических функций с помощью операции >>>=, не должно иметь значения то, как они вложены. В формальной записи выполнение >(m >>= f) >>= g – точно то же, что и выполнение >m >>= (\x –> f x >>= g).

Гм-м, что теперь тут происходит? У нас есть одно монадическое значение, >m, и две монадические функции, >f и >g. Когда мы выполняем выражение >(m >>= f) >>= g, то передаём значение >m в функцию >f, что даёт в результате монадическое значение. Затем мы передаём это новое монадическое значение функции >g. В выражении >m >>= (\x –> f x >>= g) мы берём монадическое значение и передаём его функции, которая передаёт результат применения >f x функции >g. Нелегко увидеть, почему обе эти записи равны, так что давайте взглянем на пример, который делает это равенство немного более очевидным.

Помните нашего канатоходца Пьера, который пытался удержать равновесие, в то время как птицы приземлялись на его балансировочный шест? Чтобы симулировать приземление птиц на балансировочный шест, мы создали цепочку из нескольких функций, которые могли вызывать неуспешное окончание вычислений:

>ghci> return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2

>Just (2,4)

Мы начали со значения >Just (0, 0), а затем связали это значение со следующей монадической функцией >landRight 2. Результатом было другое монадическое значение, связанное со следующей монадической функцией, и т. д. Если бы надлежало явно заключить это в скобки, мы написали бы следующее:

>ghci> ((return (0, 0) >>= landRight 2) >>= landLeft 2) >>= landRight 2

>Just (2,4)

Но мы также можем записать инструкцию вот так:

>return (0, 0) >>= (\x –>

>landRight 2 x >>= (\y –>

>landLeft 2 y >>= (\z –>

>landRight 2 z)))

Вызов >return (0, 0) – то же самое, что >Just (0, 0), и когда мы передаём это анонимной функции, образец >x принимает значение >(0, 0). Функция >landRight принимает количество птиц и шест (кортеж, содержащий числа) – и это то, что ей передаётся. В результате мы имеем значение >Just (0, 2), и, когда передаём его следующей анонимной функции, образец >y становится равен >(0, 2). Это продолжается до тех пор, пока последнее приземление птицы не вернёт в качестве результата значение