scalascala-catsinvariance

Why can find `Functor` instance for Tree but not for Branch or Leaf?


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?


Solution

  • 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.