Почему можно найти экземпляр 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)
  }
Другие вопросы по тегам