Функция параметризуется классом против


У меня был запрос о том, в чем разница между параметризацией класса и параметризацией функции.

Я обеспечил реализацию функтора следующим образом:

trait Functor[F[_],A,B] {
  def map(fa: F[A]) (f: A => B) : F[B]
}

И другой, где функция параметризуется следующим образом:

trait Functor[F[_]] {
  def map[A,B](fa: F[A]) (f: A => B) : F[B]
}

Где находятся случаи, когда мы должны использовать один над другим?

Еще один последующий вопрос: Почему мы передаем аргумент функтору как F [_], а не как F[A] или F[B]. Какие случаи возникают, когда мы используем либо F[A], либо Ф[Б]?

1 2

1 ответ:

Всегда предпочитайте второе. С помощью первого вы можете реализовать такие бессмысленные экземпляры, как:

trait WrongFunctor[F[_],A,B] {
  def map(fa: F[A])(f: A => B) : F[B]
}

case class notEvenRemotelyAFunctor[A]() extends WrongFunctor[List,A,Int] {

  def map(fa: List[A])(f: A => Int) : List[Int] = 
    if(f(fa.head) < 4) List(3) else List(4)
}

type Id[X] = X
case object ILikeThree extends WrongFunctor[Id, Int, Int] {

  def map(fa: Int)(f: Int => Int): Int = if(fa == 3) 3 else f(fa)
}

Даже если вы все делаете правильно, вам понадобится для реализации фиксированного функтора один объект на разные типы, в которых вы хотите использовать fmap. Но важно то, что второй по крайней мере затрудняет написание такого рода неправильных "функторов"; меньше нефункторов будет проскальзывать мимо:

trait Functor[F[_]] {

  def map[A,B](fa: F[A])(f: A => B) : F[B]
}
case object ILikeThreeAgain extends Functor[Id] {

  def map[A,B](fa: A)(f: A => B) : B =
    ??? // how do I write the above here?
}

Ключевыми словами здесь являются параметричность и Параметрический полиморфизм. Интуиция подсказывает, что если что-то определено обобщенно, то можно вывести свойства, которым оно будет удовлетворять, только из соответствующих типов. Смотрите, например, блог Бартоша Милевского-Параметричность: деньги ни за что и теоремы бесплатно для хорошего объяснения или канонические теоремы для бесплатной бумаги.

Последующий вопрос

Еще один последующий вопрос: почему мы передаем аргумент функтору как F[_] , а не как F[A] или F [B]. Какие случаи возникают, когда мы используем либо F[A], либо F[B]?

Потому что это часть того, что такое функтор; это "конструктор":

  1. для каждого входного типа A он дает вам в качестве выходного другой тип F[A]
  2. и для каждой функции f: A => B другую функцию fmap(f): F[A] => F[B], удовлетворяющую fmap(id[A]) == id[F[A]] и fmap(f andThen g) == fmap(f) andThen fmap(g)

Итак, для 1. вам нужен способ представления функций на типах; и это то, что есть F[_].

Обратите внимание, что имея метод map, как в ваша подпись в этом случае эквивалентна fmap:

trait Functor[F[_]] {

  def map[A,B](fa: F[A])(f: A => B) : F[B]

  def fmap[A,B](f: A => B): F[A] => F[B] =
    { fa => map(fa)(f) }

  def mapAgain[A,B](fa: F[A])(f: A => B) : F[B] =
    fmap(f)(fa)
}
Теперь о том, как это связано с реальной теорией категорий:

Экземпляры вашего Functor[F[_]] признака выше предназначены для представленияScala -обогащенных функторов

F: ScalaScala

Давай показывай.

Существует (обычно неявно определенная) категория Scala с типами объектов и функциями морфизмов f: A ⇒ B. Эта категория является декартовой замкнутой, где внутренним hom является Тип A ⇒ B и произведение (A, B). Тогда мы можем работать сScala -обогащенными категориями и функторами. Что такоеScala -обогащенная категория? в основном тот, который вы можете определить с помощью языка Scala: у вас есть

  1. набор объектов (которые нужно представить в виде типов)
  2. для каждого типа A, B A C[A,B] с тождествами id[X]: C[X,Y] и составом andThen[X,Y,Z]: (C[X,Y], C[Y,Z]) => C[X,Z], удовлетворяющими аксиомам категорий

Обогащенные функторы F: C → D тогда

  1. функция от объектов с тем, Д, A -> F[A]
  2. для каждой пары объектов A, B: C морфизм в Scala , то есть функция fmap: C[A,B] => C[F[A], F[B]], удовлетворяющая законам функтора fmap(id[A]) == id[F[A]] и fmap(f andThen g) == fmap(f) andThen fmap(g)

Scala естественно обогащается сама по себе, с Scala[X,Y] = X => Y и обогащенными функторами F: ScalaScala - это то, что должны представлять экземпляры вашего признака Functor[F[_]].

Конечно, для этого нужно все квалификация о том, как Scala нарушает то и это, равенство морфизма и т. д. Но мораль истории такова: ваш базовый язык л (как скала в данном случае), скорее всего, пытается быть декартово-замкнутой (или, по крайней мере, симметричная моноидальная-закрытые) категории и функторы определяемые через соответствующие л, обогащенной функторы.