Левое тождество и правое тождество являются, по сути, законами, которые описывают, как должна вести себя функция >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)
. Это продолжается до тех пор, пока последнее приземление птицы не вернёт в качестве результата значение