Аппликативный функтор Maybe
Давайте взглянем на реализацию экземпляра класса Applicative для типа Maybe:
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
Опять же из определения класса мы видим, что идентификатор f, который играет роль аппликативного функтора, должен принимать один конкретный тип в качестве параметра. Поэтому мы пишем instance Applicative Maybe where вместо instance Applicative (Maybe a) where.
Далее, у нас есть функция pure. Вспомните, что функция должна что-то принять и обернуть в аппликативное значение. Мы написали pure = Just, потому что конструкторы данных вроде Just являются обычными функциями. Также можно было бы написать pure x = Just x.
Наконец, у нас есть определение оператора <*>. Извлечь функцию из значения Nothing нельзя, поскольку внутри него нет функции. Поэтому мы говорим, что если мы пробуем извлечь функцию из значения Nothing, результатом будет то же самое значение Nothing.
В определении класса Applicative есть ограничение класса Functor – значит, мы можем считать, что оба параметра оператора <*> являются значениями функтора. Если первым аргументом выступает не значение Nothing, а Just с некоторой функцией внутри, то мы говорим, что с помощью данной функции хотим отобразить второй параметр. Этот код также заботится о случае, когда вторым аргументом является значение Nothing, потому что его отображение с помощью любой функции при использовании метода fmap вернёт всё то же Nothing. Итак, в случае с типом Maybe оператор <*> извлекает функцию из значения слева, если это Just, и отображает с её помощью значение справа. Если какой-либо из параметров является значением Nothing, то и результатом будет Nothing.
Теперь давайте это опробуем:
ghci> Just (+3) <*> Just 9
Just 12
ghci> pure (+3) <*> Just 10
Just 13
ghci> pure (+3) <*> Just 9
Just 12
ghci> Just (++"ха-ха") <*> Nothing Nothing
ghci> Nothing <*> Just "во-от"
Nothing
Вы видите, что выполнение выражений pure (+3) и Just (+3) в данном случае – одно и то же. Используйте функцию pure, если имеете дело со значениями типа Maybe в аппликативном контексте (если вы используете их с оператором <*>); в противном случае предпочитайте конструктор Just.
Первые четыре введённых строки демонстрируют, как функция извлекается, а затем используется для отображения; но в данном случае этого можно было добиться, просто применив не обёрнутые функции к функторам. Последняя строка любопытна тем, что мы пытаемся извлечь функцию из значения Nothing, а затем отображаем с её помощью нечто, что в результате даёт Nothing.
Когда вы отображаете функтор с помощью функции при использовании обычных функторов, вы не можете извлечь результат каким-либо общим способом, даже если результатом является частично применённая функция. Аппликативные функторы, с другой стороны, позволяют вам работать с несколькими функторами, используя одну функцию.