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

и >landRight.

Необходимо дать функциям >landLeft и >landRight возможность завершаться неуспешно. Нам нужно, чтобы они возвращали новый шест, если равновесие поддерживается, но завершались неуспешно, если птицы приземляются неравномерно. И какой способ лучше подойдёт для добавления к значению контекста неудачи, чем использование типа >Maybe? Давайте переработаем эти функции:

>landLeft :: Birds –> Pole –> Maybe Pole

>landLeft n (left,right)

>   | abs ((left + n) - right) < 4 = Just (left + n, right)

>   | otherwise                    = Nothing


>landRight :: Birds –> Pole –> Maybe Pole

>landRight n (left,right)

>   | abs (left - (right + n)) < 4 = Just (left, right + n)

>   | otherwise                    = Nothing

Вместо того чтобы вернуть значение типа >Pole, эти функции теперь возвращают значения типа >Maybe Pole. Они по-прежнему принимают количество птиц и прежний шест, как и ранее, но затем проверяют, выведет ли Пьера из равновесия приземление такого количества птиц. Мы используем охранные выражения, чтобы проверить, меньше ли разница в количестве птиц на новом шесте, чем 4. Если меньше, оборачиваем новый шест в конструктор >Just и возвращаем это. Если не меньше, возвращаем значение >Nothing, сигнализируя о неудаче.

Давайте опробуем этих деток:

>ghci> landLeft 2 (0, 0)

>Just (2,0)

>ghci> landLeft 10 (0, 3)

>Nothing

Когда мы приземляем птиц, не выводя Пьера из равновесия, мы получаем новый шест, обёрнутый в конструктор >Just. Но когда значительное количество птиц в итоге оказывается на одной стороне шеста, в результате мы получаем значение >Nothing. Всё это здорово, но, похоже, мы потеряли возможность многократного приземления птиц на шесте! Выполнить >landLeft 1 (landRight 1 (0, 0)) больше нельзя, потому что когда >landRight 1 применяется к >(0, 0), мы получаем значение не типа >Pole, а типа >Maybe Pole. Функция >landLeft 1 принимает параметр типа >Pole, а не >Maybe Pole.

Нам нужен способ получения >Maybe Pole и передачи его функции, которая принимает >Pole и возвращает >Maybe Pole. К счастью, у нас есть операция >>>=, которая делает именно это для типа >Maybe. Давайте попробуем:

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

>Just (2,1)

Вспомните, что функция >landLeft 2 имеет тип >Pole –> Maybe Pole. Мы не можем просто передать ей значение типа >Maybe Pole, которое является результатом вызова функции >landRight 1 (0, 0), поэтому используем операцию >>>=, чтобы взять это значение с контекстом и отдать его функции >landLeft 2. Операция >>>= действительно позволяет нам обрабатывать значения типа >Maybe как значения с контекстом. Если мы передадим значение