При использовании класса типов >Applicative
мы можем последовательно задействовать несколько операторов ><*>
в виде цепочки вызовов, что позволяет легко работать сразу с несколькими аппликативными значениями, а не только с одним. Взгляните, например, на это:
>ghci> pure (+) <*> Just 3 <*> Just 5
>Just 8
>ghci> pure (+) <*> Just 3 <*> Nothing
>Nothing
>ghci> pure (+) <*> Nothing <*> Just 5
>Nothing
Мы обернули оператор >+
в аппликативное значение, а затем использовали оператор ><*>
, чтобы вызвать его с двумя параметрами, оба из которых являются аппликативными значениями.
Давайте посмотрим, как это происходит, шаг за шагом. Оператор ><*>
левоассоциативен; это значит, что
>pure (+) <*> Just 3 <*> Just 5
то же самое, что и вот это:
>(pure (+) <*> Just 3) <*> Just 5
Сначала оператор >+
помещается в аппликативное значение – в данном случае значение типа >Maybe
, которое содержит функцию. Итак, у нас есть >pure (+)
, что, по сути, равно >Just (+)
. Далее происходит вызов >Just (+) <*> Just 3
. Его результатом является >Just (3+)
. Это из-за частичного применения. Применение только значения >3
к оператору >+
возвращает в результате функцию, которая принимает один параметр и добавляет к нему >3
. Наконец, выполняется >Just (3+) <*> Just 5
, что в результате возвращает >Just 8
.
Ну разве не здорово?! Аппликативные функторы и аппликативный стиль вычисления >pure f <*> x <*> y <*>
… позволяют взять функцию, которая ожидает параметры, не являющиеся аппликативными значениями, и использовать эту функцию для работы с несколькими аппликативными значениями. Функция может принимать столько параметров, сколько мы захотим, потому что она всегда частично применяется шаг за шагом между вхождениями оператора ><*>
.
Это становится ещё более удобным и очевидным, если мы примем во внимание тот факт, что выражение >pure f <*> x
равно >fmap f x
. Это один из законов аппликативных функторов, которые мы более подробно рассмотрим чуть позже; но давайте подумаем, как он применяется здесь. Функция >pure
помещает значение в контекст по умолчанию. Если мы просто поместим функцию в контекст по умолчанию, а затем извлечём её и применим к значению внутри другого аппликативного функтора, это будет то же самое, что просто отобразить этот аппликативный функтор с помощью данной функции. Вместо записи