Аппликативный функтор 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.

Когда вы отображаете функтор с помощью функции при использовании обычных функторов, вы не можете извлечь результат каким-либо общим способом, даже если результатом является частично применённая функция. Аппликативные функторы, с другой стороны, позволяют вам работать с несколькими функторами, используя одну функцию.