Когда мы впервые заговорили о функторах в главе 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
Вы также видели, что можно взять обычное значение и обернуть его в тип данных. Например, мы можем взять значение