Почему можно найти экземпляр Functor для Tree, но не для Branch или Leaf?
У меня есть следующее определение Functor:
import cats.Functor
import cats.syntax.functor._
object Theory {
implicit val treeFunctor: Functor[Tree] =
new Functor[Tree] {
def map[A, B](fa: Tree[A])(f: A => B): Tree[B] =
fa match {
case Branch(left, right) =>
Branch(map(left)(f), map(right)(f))
case Leaf(value) =>
Leaf(f(value))
}
}
def main(args: Array[String]): Unit = {
Branch(Leaf(10), Leaf(20)).map(_ * 2)
}
}
за:
sealed trait Tree[+A]
final case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
final case class Leaf[A](value: A) extends Tree[A]
Почему компилятор жалуется:
// <console>:42: error: value map is not a member of wrapper.Branch[
Int]
//
Branch(Leaf(10), Leaf(20)).map(_ * 2)
//
Поэтому я должен создать умный конструктор:
object Tree {
def branch[A](left: Tree[A], right: Tree[A]): Tree[A] =
Branch(left, right)
def leaf[A](value: A): Tree[A] =
Leaf(value)
}
Что такое умный конструктор в этом случае?
2 ответа
Декларация Functor[F[_]]
в cats
инвариантен в F
, Следовательно, Functor[Tree]
не является ни обобщением, ни специализацией Functor[Branch]
, Эти типы не связаны.
Проблема с вашим кодом заключается в следующем. Выражение
Branch(Leaf(10), Leaf(20))
имеет тип Branch[Int]
, Когда вы пытаетесь подать заявку .map[X]
прямо к нему, вы сигнализируете, что хотели бы получить Branch[X]
в результате. Но нет Functor[Branch]
по объему (это не так, как вы не могли бы написать, но в настоящее время нет).
Для того, чтобы использовать Functor[Tree]
, вы должны дать понять компилятору, что вы хотите рассматривать этот экземпляр как Tree[Int]
, Кастинг будет работать. Или используя пользовательский метод фабрики, который скрывает Branch
и выставляет Tree
будет работать тоже: это то, что делает "умный" конструктор.
Ты можешь использовать kittens
и реализовать экземпляры для Branch
а также Leaf
затем экземпляр для Tree
можно вывести.
libraryDependencies += "org.typelevel" %% "kittens" % "1.0.0-RC2"
import cats.Functor
import cats.syntax.functor._
sealed trait Tree[+A]
final case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
final case class Leaf[A](value: A) extends Tree[A]
implicit val treeFunctor: Functor[Tree] = cats.derive.functor[Tree]
implicit val branchFunctor: Functor[Branch] =
new Functor[Branch] {
def map[A, B](fa: Branch[A])(f: A => B): Branch[B] =
fa match {
case Branch(left, right) =>
Branch(left.map(f), right.map(f))
}
}
// or without extension method
// implicit def branchFunctor(implicit treeFunctor: Functor[Tree]): Functor[Branch] =
// new Functor[Branch] {
// def map[A, B](fa: Branch[A])(f: A => B): Branch[B] =
// fa match {
// case Branch(left, right) =>
// Branch(treeFunctor.map(left)(f), treeFunctor.map(right)(f))
// }
// }
implicit val leafFunctor: Functor[Leaf] =
new Functor[Leaf] {
def map[A, B](fa: Leaf[A])(f: A => B): Leaf[B] =
fa match {
case Leaf(value) =>
Leaf(f(value))
}
}
def main(args: Array[String]): Unit = {
(Branch(Leaf(10), Leaf(20)): Tree[Int]).map(_ * 2)
Branch(Leaf(10), Leaf(20)).map(_ * 2)
Leaf(10).map(_ * 2)
}
На самом деле тогда вы можете получить все три экземпляра:
implicit val treeFunctor: Functor[Tree] = cats.derive.functor[Tree]
implicit val leafFunctor: Functor[Leaf] = cats.derive.functor[Leaf]
implicit val branchFunctor: Functor[Branch] = cats.derive.functor[Branch]
def main(args: Array[String]): Unit = {
(Branch(Leaf(10), Leaf(20)): Tree[Int]).map(_ * 2)
Branch(Leaf(10), Leaf(20)).map(_ * 2)
Leaf(10).map(_ * 2)
}