Аппликативы
Сегодня поговорим об аппликативных функторах и о том какие проблемы они решают.
Для понимания лучше прочитать статью о функторах: https://medium.com/@gubinrobert/%D1%84%D1%83%D0%BD%D0%BA%D1%82%D0%BE%D1%80%D1%8B-f9f826539a0d
Начнем с проблематики: довольно часто на практике нужно применить функцию о множестве аргументов к содержимому множества контейнеров. Например просуммировать их содержимое. Хотелось бы что-то вроде:
map2(Maybe(2), Maybe(3), ((a, b) => a + b))
В сущности это становится возможным благодаря интересному трюку — решению проблемы следующего вида:
def ap[F[_], A, B](f: F[A => B])(v: F[A]): F[B]Как видно из определения — морфизм, поднятый в категорию F, это то же самое что и морфизм из F[A] => F[B].
Итак, что это нам даёт? И как поднять функцию в категорию F?
Ответ на этот вопрос может быть понятен из следующего примера:
Стоит обратить внимания что функция f — по сути каррированная функция от двух аргументов. Результатом её применения посредством функции map будет являться функция, но уже от одного аргумента.
Таким образом мы и поднимаем (lift) функцию. Можно посмотреть на это через призму lift:
Как видно результаты идентичны.
Теперь, когда у нас есть функция вида Maybe[Int => Int] как же использовать её?
По сути нам нужно как-то сделать из Maybe[Int => Int] функцию вида Maybe[Int] => Maybe[Int].
Тут на помощь и приходят аппликативы:
Далее мы напишем код, соответствующий аппликативу и рассмотрим еще несколько функций, которые можно выразить в терминах ap.
Из определения видно следующее:
- для того чтобы быть аппликативным функтором нужно сначала стать функтором
- аппликативный функтор требует наличия функции pure, которая поднимает объект в категорию для которой определен функтор
- в терминах ap можно выразить такие интересные функции как product и map2.
функция product — делает из двух контейнеров один с кортежем внутри
функция map2 — композиция функций product и map, именно та, которую мы и хотели в начале статьи.
Теперь напишем реализацию этого трейта:
По сути всё что мы делаем — это берем функцию и применяем её к значению, если функции нет, то и применить к значению мы ничего не сможем,
соответственно и все функторные законы должны исполняться:
Таким образом аппликативные функторы — невероятно полезная сущность, благодаря которой можно избавиться от кучи лишнего кода и сделать решение более лаконичным и понятным.
Материалы:
http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors