I have the following Functor definition:
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)
}
}
for:
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]
Why the compiler complains:
// <console>:42: error: value map is not a member of wrapper.Branch[
Int]
//
Branch(Leaf(10), Leaf(20)).map(_ * 2)
//
So I have to create a smart constructor:
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)
}
What is a smart constructor in this case?
The declaration of Functor[F[_]]
in cats
is invariant in F
.
Therefore, a Functor[Tree]
is neither a generalization, nor a specialization of Functor[Branch]
. These types are unrelated.
The problem with your code is the following. The expression
Branch(Leaf(10), Leaf(20))
is of type Branch[Int]
. When you try to apply .map[X]
to it directly, you signal that you would like to get a Branch[X]
as the result. But there is no Functor[Branch]
in scope (it's not like you couldn't write one, but as it stands, there is none).
In order to make use of the Functor[Tree]
, you have to make it clear to the compiler that you want to treat this instance as Tree[Int]
. Casting would work. Or using a custom factory method that hides Branch
and exposes Tree
would work too: that's what the "smart" constructor is doing.