我跟随ADT:
import cats.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 def treeFunctor = 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(v) => Leaf(f(v))
}
}
并按如下方式使用:
val b = Branch(left = Branch(Branch(Leaf(5), Leaf(3)), Leaf(10)), Leaf(45))
val functorTree = Functor[Tree].map(b)((v) => v * 4)
但是:
Branch(Leaf(10), Leaf(20)).map(_ * 2)
我收到了编译错误:
Error:(21, 29) value map is not a member of A$A90.this.Branch[Int]
Branch(Leaf(10), Leaf(20)).map(_ * 2)
^
我的问题是为什么我会收到错误?
答案 0 :(得分:3)
方法treeFunctor
为您提供Functor[Tree]
的{{1}}实例,但这并不意味着任何Tree
会自动转换为Tree
。只是对于任何Tree
,您在范围内都有Functor[Tree]
的隐式实例。
这个隐式实例有一个方法map
,它有两个参数:映射的Functor[Tree]
实例,以及映射函数本身。
所以这应该有效:
implicitly[Functor[Tree]].map(Branch(Leaf(10), Leaf(20)))(_ * 2)
或只是
Functor[Tree].map(Branch(Leaf(10), Leaf(20)))(_ * 2)
(因为scalaz和猫通常有类型类的方便apply())
编辑:在阅读您的问题时,我错过了Functor[Tree].map(b)((v) => v * 4)
部分。所以你真正感兴趣的是以下部分。
如果要保留调用map
的语法,可以创建一个隐式类作为包装器:
implicit class toTreeFunctor[A](tree: Tree[A]) {
def map[B](f: A => B) = Functor[Tree].map(tree)(f)
}
Branch(Leaf(10), Leaf(20)).map(_ * 2)
正如@P指出的那样。 Frolov,您可以从import scalaz.syntax.functor._
或import cats.syntax.functor._
免费获得此隐式转化,但请注意,您必须明确地将Branch
转发为Tree
的实例。
答案 1 :(得分:2)
由于您正在使用Cats,因此您可以import cats.syntax.functor._
获取map
。请注意,您需要将分支转换为基本类型,因为Functor
实例是为Tree
定义的,而不仅仅是单独的Branch
。您可以在随播对象中使用类型注释或智能构造函数来执行此操作。
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 def treeFunctor = 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(v) => Leaf(f(v))
}
}
val tree: Tree[Int] = Branch(Leaf(10), Leaf(20))
tree.map(_ * 2) // Branch(Leaf(20), Leaf(40))
cats.syntax.functor
实际上只是一个扩展FunctorSyntax
特征的单例对象,包含从实现Functor
的类到包装器的隐式转换,就像在slouc的答案中一样。包装器本身Functor.Ops
由Simulacrum @typeclass
宏生成。