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

Итак, встречайте класс типов >Applicative, находящийся в модуле >Control.Applicative!.. Он определяет две функции: >pure и ><*>. Он не предоставляет реализации по умолчанию для какой-либо из этих функций, поэтому нам придётся определить их обе, если мы хотим, чтобы что-либо стало аппликативным функтором. Этот класс определён вот так:

>class (Functor f) => Applicative f where

>   pure :: a –> f a

>   (<*>) :: f (a –> b) –> f a –> f b

Простое определение класса из трёх строк говорит нам о многом!.. Первая строка начинается с определения класса >Applicative; также она вводит ограничение класса. Ограничение говорит, что если мы хотим определить для типа экземпляр класса >Applicative, он, прежде всего, уже должен иметь экземпляр класса >Functor. Вот почему, когда нам известно, что конструктор типа принадлежит классу >Applicative, можно смело утверждать, что он также принадлежит классу >Functor, так что мы можем применять к нему функцию >fmap.

Первый метод, который он определяет, называется >pure. Его сигнатура выглядит так: >pure :: a –> f a. Идентификатор >f играет здесь роль нашего экземпляра аппликативного функтора. Поскольку язык Haskell обладает очень хорошей системой типов и притом всё, что может делать функция, – это получать некоторые параметры и возвращать некоторое значение, мы можем многое сказать по объявлению типа, и данный тип – не исключение.

Функция >pure должна принимать значение любого типа и возвращать аппликативное значение с этим значением внутри него. Словосочетание «внутри него» опять вызывает в памяти нашу аналогию с коробкой, хотя мы и видели, что она не всегда выдерживает проверку. Но тип >a –> f a всё равно довольно нагляден. Мы берём значение и оборачиваем его в аппликативное значение, которое содержит в себе это значение в качестве результата. Лучший способ представить себе функцию >pure – это сказать, что она берёт значение и помещает его в некий контекст по умолчанию (или чистый контекст) – минимальный контекст, который по-прежнему возвращает это значение.

Оператор ><*> действительно интересен. У него вот такое определение типа:

>f (a –> b) –> f a –> f b

Напоминает ли оно вам что-нибудь? Оно похоже на сигнатуру >fmap>::>(a>–>>b)>–>>f>a>–>>f>b. Вы можете воспринимать оператор ><*> как разновидность расширенной функции >fmap. Тогда как функция >fmap принимает функцию и значение функтора и применяет функцию внутри значения функтора, оператор ><*> принимает значение функтора, который содержит в себе функцию, и другой функтор – и извлекает эту функцию из первого функтора, затем отображая с её помощью второй.