Инвариантный функтор

Формальное определение

Инвариантный функтор поддерживает операцию xmap (или imap), которая преобразует F[A] в F[B] с учетом двух функций: A => B и B => A.

Если функтор создает новые экземпляры класса типов, добавляя функцию в конец цепочки преобразований, а контравариантный функтор создает их, добавляя функцию в начало цепочки преобразований, то инвариантный функтор создает их с помощью пары двунаправленных преобразований.

Инвариантный функтор должен удовлетворять двум законам:

Также известен как экспоненциальный функтор.

Определение в виде кода на Scala

trait InvariantFunctor[F[_]]:
  extension [A](fa: F[A]) 
    def xmap[B](f: A => B, g: B => A): F[B]

Примеры

"Обертка"

import cats.Id

given InvariantFunctor[Id] with
  extension [A](fa: Id[A])
    override def xmap[B](f: A => B, g: B => A): Id[B] = Id(f(fa))

Докажем, что Id удовлетворяет законам инвариантного функтора.

Codec

Зачастую инвариантный функтор используется в кодеках, кодирующих и декодирующих строки.

trait Codec[A]:
  def encode(value: A): String
  def decode(value: String): A

given InvariantFunctor[Codec] with
  extension [A](fa: Codec[A])
    override def xmap[B](f: A => B, g: B => A): Codec[B] =
      new Codec[B]:
        def encode(value: B): String = fa.encode(g(value))

        def decode(value: String): B = f(fa.decode(value))

Докажем, что Codec удовлетворяет законам инвариантного функтора.


Ссылки: