Когда мы впервые заговорили о функторах в главе 7, вы видели, что они являются полезной концепцией для значений, которые можно отображать. Затем в главе 11 мы развили эту концепцию с помощью аппликативных функторов, которые позволяют нам воспринимать значения определённых типов данных как значения с контекстами и применять к этим значениям обычные функции, сохраняя смысл контекстов.
В этой главе вы узнаете о монадах, которые, по сути, представляют собой расширенные аппликативные функторы, так же как аппликативные функторы являются всего лишь расширенными функторами.
Совершенствуем наши аппликативные функторы
Когда мы начали с функторов, вы видели, что можно отображать разные типы данных с помощью функций, используя класс типов >Functor
. Введение в функторы заставило нас задаться вопросом: «Когда у нас есть функция типа >a –> b
и некоторый тип данных >f a
, как отобразить этот тип данных с помощью функции, чтобы получить значение типа >f b
?» Вы видели, как с помощью чего-либо отобразить >Maybe a
, список >[a]
, >IO a
и т. д. Вы даже видели, как с помощью функции типа >a –> b
отобразить другие функции типа >r –> a
, чтобы получить функции типа >r –> b
. Чтобы ответить на вопрос о том, как отобразить некий тип данных с помощью функции, нам достаточно было взглянуть на тип функции >fmap
:
>fmap :: (Functor f) => (a –> b) –> f a –> f b
А затем нам необходимо было просто заставить его работать с нашим типом данных, написав соответствующий экземпляр класса >Functor
.
Потом вы узнали, что возможно усовершенствование функторов, и у вас возникло ещё несколько вопросов. Что если эта функция типа >a –> b
уже обёрнута в значение функтора? Скажем, у нас есть >Just (*3)
– как применить это к значению >Just 5
? Или, может быть, не к >Just 5
, а к значению >Nothing
? Или, если у нас есть список >[(*2),(+4)]
, как применить его к списку >[1,2,3]
? Как это вообще может работать?.. Для этого был введён класс типов >Applicative
:
>(<*>) :: (Applicative f) => f (a –> b) –> f a –> f b
Вы также видели, что можно взять обычное значение и обернуть его в тип данных. Например, мы можем взять значение