Тип Maybe
функционирует в качестве аппликативного функтора аналогично. Однако при использовании аппликативных функторов сама функция находится в контексте наряду со значением, к которому она применяется. Тип Maybe
является аппликативным функтором таким образом, что когда мы используем операцию <*>
для применения функции внутри типа Maybe
к значению, которое находится внутри типа Maybe
, они оба должны быть значениями Just
, чтобы результатом было значение Just
; в противном случае результатом будет значение Nothing
. Это имеет смысл. Если недостаёт функции либо значения, к которому вы её применяете, вы не можете ничего получить «из воздуха», поэтому вы должны распространить неудачу.
ghci> Just (+3) <*> Just 3
Just 6
ghci> Nothing <*> Just "алчность"
Nothing
ghci> Justord <*> Nothing
Nothing
Использование аппликативного стиля, чтобы обычные функции работали со значениями типа Maybe
, действует аналогичным образом. Все значения должны быть значениями Just
; в противном случае всё это напрасно (Nothing
)!
ghci> max <$> Just 3 <*> Just 6
Just 6
ghci> max <$> Just 3 <*> Nothing
Nothing
А теперь давайте подумаем над тем, как бы мы использовали операцию >>=
с типом Maybe
. Операция >>=
принимает монадическое значение и функцию, которая принимает обычное значение. Она возвращает монадическое значение и умудряется применить эту функцию к монадическому значению. Как она это делает, если функция принимает обычное значение? Ну, она должна принимать во внимание контекст этого монадического значения.
В данном случае операция >>=
принимала бы значение типа Maybe a
и функцию типа a –> Maybe b
и каким-то образом применяла бы эту функцию к значению Maybe a
. Чтобы понять, как она это делает, мы будем исходить из того, что тип Maybe
является аппликативным функтором. Скажем, у нас есть анонимная функция \x –> Just (x+1)
. Она принимает число, прибавляет к нему 1
и оборачивает его в конструктор Just
:
ghci> (\x –> Just (x+1)) 1
Just 2
ghci> (\x –> Just (x+1)) 100
Just 101
Если мы передадим ей значение 1
, она вернёт результат Just 2
. Если мы дадим ей значение 100
, результатом будет Just 101
. Это выглядит очень просто. Но как нам передать этой функции значение типа Maybe
? Если мы подумаем о том, как тип Maybe
работает в качестве аппликативного функтора, ответить на этот вопрос будет довольно легко. Мы передаём функции значение Just
, берём то, что находится внутри конструктора Just
, и применяем к этому функцию. Если мы даём ей значение Nothing
, то у нас остаётся функция, но к ней нечего (Nothing
) применить. В этом случае давайте сделаем то же, что мы делали и прежде, и скажем, что результат равен Nothing
.
Вместо того чтобы назвать функцию >>=
, давайте пока назовём её applyMaybe
. Она принимает значение типа Maybe a
и функцию, которая возвращает значение типа Maybe b
, и умудряется применить эту функцию к значению типа Maybe a
. Вот она в исходном коде:
applyMaybe :: Maybe a –> (a –> Maybe b) –> Maybe b
applyMaybe Nothing f = Nothing
applyMaybe (Just x) f = f x
Теперь давайте с ней поиграем. Мы будем использовать её как инфиксную функцию так, чтобы значение типа Maybe
было слева, а функция была справа:
ghci> Just 3 `applyMaybe` \x –> Just (x+1)
Just 4
ghci> Just "смайлик" `applyMaybe` \x –> Just (x ++ " :)")
Just "смайлик :)"
ghci> Nothing `applyMaybe` \x –> Just (x+1)
Nothing
ghci> Nothing `applyMaybe` \x –> Just (x ++ " :)")
Nothing
В данном примере, когда мы использовали функцию applyMaybe
со значением Just
и функцией, функция просто применялась к значению внутри конструктора Just
. Когда мы попытались использовать её со значением Nothing
, весь результат был равен Nothing
. Что насчёт того, если функция возвращает Nothing
? Давайте посмотрим:
ghci>Just 3 `applyMaybe` \x –> if x > 2 then Just x else Nothing
Just 3
ghci> Just 1 `applyMaybe` \x –> if x > 2 then Just x else Nothing
Nothing