scalabook

Форк
0
/
invariant-functor.md 
96 строк · 4.5 Кб

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

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

Инвариантный функтор поддерживает операцию xmap

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

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

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

  • Identity (тождественность): Если определен метод идентификации identity
    такой, что: identity(a) == a
    , тогда xmap(ma)(identity, identity) == ma
    .
  • Composition (композиция) - xmap(xmap(ma, f1, g1), f2, g2) == xmap(ma, f2 compose f1, g1 compose g2)

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

Определение в виде кода на 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

удовлетворяет законам инвариантного функтора.

  • тождественность: xmap(ma)(identity, identity) == ma
    • по определению метода xmap
      получим: Id(a).xmap(identity, identity) == Id(identity(a)) == Id(a)
  • композиция: xmap(xmap(ma, f1, g1), f2, g2) == xmap(ma, f2 compose f1, g1 compose g2)
    • по определению метода xmap
      получим: Id(a).xmap(f1, g1).xmap(f2, g2) == Id(f1(a)).xmap(f2, g2) == Id(f2(f1(a)))
      и Id(a).xmap(f2 compose f1, g1 compose g2) == Id(f2(f1(a)))

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

удовлетворяет законам инвариантного функтора.

  • тождественность: xmap(ma)(identity, identity) == ma
    • по определению метода xmap
      получим: fa.xmap(identity, identity) ==
      def encode(value: A): String = fa.encode(identity(value)) = fa.encode(value)
      def decode(value: String): A = identity(fa.decode(value)) = = fa.decode(value)
      Это эквивалентно исходному fa
      (в случае использования чистых функций) - проксируются его методы.
  • композиция: xmap(xmap(ma, f1, g1), f2, g2) == xmap(ma, f2 compose f1, g1 compose g2)
    • по определению метода xmap
      получим xmap(xmap(ma, f1, g1), f2, g2) ==
      :
      def encode(value: C): String = fb.encode(g2(value)) = fa.encode(g1(g2(value)))
      def decode(value: String): C = f2(fb.decode(value)) = f2(f1(fa.decode(value)))
      xmap(ma, f2 compose f1, g1 compose g2) ==
      :
      def encode(value: C): String = fa.encode(g1 compose g2) = fa.encode(g1(g2(value)))
      def decode(value: String): C = (f2 compose f1)(fa.decode(value)) = f2(f1(fa.decode(value)))

Ссылки:

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.