Ага-а, вложенное использование операции >>=
! Во внешней анонимной функции мы передаём значение Just "!"
анонимной функции \y –> Just (show x ++ y)
. Внутри этой анонимной функции параметр y
становится равным "!"
. Параметр x
по-прежнему равен 3
, потому что мы получили его из внешней анонимной функции. Всё это как будто напоминает мне о следующем выражении:
ghci> let x = 3; y = "!" in show x ++ y
"3!"
Главное отличие состоит в том, что значения в нашем примере с использованием оператора >>=
являются монадическими. Это значения с контекстом неудачи. Мы можем заменить любое из них на неудачу:
ghci> Nothing >>= (\x –> Just "!" >>= (\y –> Just (show x ++ y))) Nothing
ghci> Just 3 >>= (\x –> Nothing >>= (\y –> Just (show x ++ y)))
Nothing
ghci> Just 3 >>= (\x –> Just "!" >>= (\y –> Nothing))
Nothing
В первой строке передача значения Nothing
функции естественным образом даёт в результате Nothing
. Во второй строке мы передаём значение Just 3
функции, и параметр x
становится равным 3
. Но потом мы передаём значение Nothing
внутренней анонимной функции, и результатом становится Nothing
, что заставляет внешнюю анонимную функцию тоже произвести Nothing
в качестве своего результата. Это что-то вроде присвоения значений переменным в выражениях let
, только значения, о которых идёт речь, являются монадическими.
Чтобы проиллюстрировать эту идею, давайте запишем следующие строки в сценарий так, чтобы каждое значение типа Maybe
занимало свою собственную строку:
foo :: Maybe String
foo = Just 3 >>= (\x –>
Just "!">>= (\y –>
Just (show x ++ y)))
Чтобы уберечь нас от написания всех этих раздражающих анонимных функций, язык Haskell предоставляет нам нотацию do
. Она позволяет нам записать предыдущий кусок кода вот так:
foo :: Maybe String
foo = do
x <– Just 3
y <– Just "!"
Just (show x ++ y)
Могло показаться, что мы получили возможность временно извлекать сущности из значений типа Maybe
без необходимости проверять на каждом шагу, являются ли значения типа Maybe
значениями в конструкторе Just
или значениями Nothing
. Вот классно!.. Если какое-либо из значений, которые мы пытаемся извлечь, равно Nothing
, всё выражение do
в результате вернёт значение Nothing
. Мы выдёргиваем наружу их значения (если таковые существуют) и перекладываем необходимость беспокойства о контексте, идущем с этими значениями, на плечи оператора >>=
.
Выражения do
– это просто другой синтаксис для сцепления монадических значений.
Делай как я
В выражении do
каждая строка, не являющаяся строкой let
, является монадическим значением. Чтобы просмотреть её результат, мы используем символ <–
. Если у нас есть значение типа Maybe String
и мы привязываем её к образцу с помощью символа <–
, этот образец будет иметь тип String
так же, как когда мы использовали операцию >>=
для передачи монадических значений анонимным функциям.
Последнее монадическое значение в выражении do
– такое как Just (show x ++ y)
в этом примере – не может быть использовано с символом <–
для привязки его результата, потому что если бы мы преобразовали выражение do
обратно в цепочку применений оператора >>=
, это не имело бы смысла. Наоборот, результат последнего выражения является результатом всего склеенного монадического значения, учитывая возможную неудачу вычисления каждого из предыдущих монадических значений. Рассмотрите, например, следующую строку:
ghci> Just 9 >>= (\x –> Just (x > 8))
Just True
Поскольку левым параметром функции >>=
является значение в конструкторе Just
, анонимная функция применяется к значению 9
, и результатом становится значение Just True
. Мы можем переписать это в нотации do
следующим образом:
marySue :: Maybe Bool
marySue = do
x <– Just 9
Just (x > 8)
Сравнивая оба варианта, легко увидеть, почему результатом всего монадического значения является результат последнего монадического значения в выражении do
со всеми предыдущими монадическими значениями, сцепленными с ним.
Пьер возвращается