Сегодня поговорим о еще одном функторе — инвариантном (Invariant Functor). Уже было несколько постов о ковариантных функторах (называемых просто "функторами") и контравариантных функторах. Если концепция ковариантных и контравариантных функторов вам понятна, то с инвариантным все будет просто — он сочетает в себе функциональность обоих вышеупомянутых функторов. Как вы помните, с помощью функторов мы можем отображать один тип в другой с помощью функции f: С контравариантными функторами мы работаем также, но меняем местами типы в f: Это бывает полезно, когда нужно предоставить новые неявные реализации какого-то тайпкласса, повторно используя имеющиеся имплементации. Короче говоря, map в функторе используется, если мы добавляем операции в конец последовательности, а contramap в контравариантном функторе, когда мы хотим добавить их в начало. Для инвариантного функтора есть еще третий вариант — imap, который позволяет работать в обоих направлениях. Инвариантные функторы реализуют метод imap,который эквивалентен комбинации map и contramap. Метод imap — генерирует новые тайпклассы с помощью пары двунаправленных преобразований. Самый простой и распространенный пример инвариантных функторов — это кодеки и парсеры, которые выполняют преобразование в обоих направлениях. В библиотеке Cats определение imap в инвариантном функторе выглядит аналогично: Можно реализовать наш imap в терминах методов encode и decode: Таким образом, имея реализации методов encode/decode, мы можем предоставить imap, который позволит нам легко создавать новые экземпляры CustomParser для других типов: Допустим, у нас есть реализация CustomParser для Long в нашей неявной области видимости, и мы хотим получить другой парсер для java.util.Date. Мы знаем, как преобразовать Date в Long и обратно, поэтому, используя идею инвариантного функтора, мы можем легко создать новый парсер: Это похоже на уже обсуждавшиеся ранее ковариантные и контравариантные функторы. Кстати, ковариантный и контравариантный функторы на самом деле являются потомками инвариантного функтора, другими словами, функция imap может быть реализована для них с помощью их map или contramap соответственно. Про Invariant из документации Cats: Каждый ковариантный (а также контравариантный) функтор порождает инвариантный функтор, игнорируя функцию g (или, в случае контравариантности, f). где f и g соответствуют нашим функциям dec и enc. В документации Cats упоминается еще один хороший пример создания новых экземпляров Invariant с использованием тайпкласса Semigroup. Используя Semigroup[Long] из Cats, мы можем легко добавлять новые экземпляры Semigroup для новых типов, если знаем, как преобразовать один тип в другой и обратно. В примере используются преобразования Long -> Date и Date -> Long, использованные ранее: Материал подготовлен в рамках курса "Scala-разработчик". Всех желающих приглашаем на открытый урок «Разработка простого REST API c помощью HTTP4S и ZIO». На примере построения простого веб сервиса с REST API, разберем основные компоненты (пути, бизнес логика, доступ к данным, документация), а также посмотрим как дружат такие функциональные библиотеки, как http4s, cats, zio в рамках одного приложения. РЕГИСТРАЦИЯ Редактор