Функция параметризуется классом против
У меня был запрос о том, в чем разница между параметризацией класса и параметризацией функции.
Я обеспечил реализацию функтора следующим образом:
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 ответ:
Всегда предпочитайте второе. С помощью первого вы можете реализовать такие бессмысленные экземпляры, как:
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]?
Потому что это часть того, что такое функтор; это "конструктор":
- для каждого входного типа
A
он дает вам в качестве выходного другой типF[A]
- и для каждой функции
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: Scala → Scala
Давай показывай.
Существует (обычно неявно определенная) категория Scala с типами объектов и функциями морфизмов f: A ⇒ B. Эта категория является декартовой замкнутой, где внутренним hom является Тип A ⇒ B и произведение (A, B). Тогда мы можем работать сScala -обогащенными категориями и функторами. Что такоеScala -обогащенная категория? в основном тот, который вы можете определить с помощью языка Scala: у вас есть
- набор объектов (которые нужно представить в виде типов)
- для каждого типа 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 тогда
- функция от объектов с тем, Д,
A -> F[A]
- для каждой пары объектов 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: Scala → Scala - это то, что должны представлять экземпляры вашего признакаFunctor[F[_]]
.Конечно, для этого нужно все квалификация о том, как Scala нарушает то и это, равенство морфизма и т. д. Но мораль истории такова: ваш базовый язык л (как скала в данном случае), скорее всего, пытается быть декартово-замкнутой (или, по крайней мере, симметричная моноидальная-закрытые) категории и функторы определяемые через соответствующие л, обогащенной функторы.