I have following 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]
and do implementation in the functor:
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))
}
}
and uses it as follow:
val b = Branch(left = Branch(Branch(Leaf(5), Leaf(3)), Leaf(10)), Leaf(45))
val functorTree = Functor[Tree].map(b)((v) => v * 4)
But following:
Branch(Leaf(10), Leaf(20)).map(_ * 2)
I've got the compiler error:
Error:(21, 29) value map is not a member of A$A90.this.Branch[Int]
Branch(Leaf(10), Leaf(20)).map(_ * 2)
^
My question is why do I get the error?
Method treeFunctor gives you an instance of Functor[Tree] for any Tree, but that doesn't mean that any Tree is automatically converted to it. It's just that for any Tree, you have an implicit instance of Functor[Tree] in the scope.
This implicit instance has a method map which takes two parameters: instance of Functor[Tree] to map over, and the mapping function itself.
So this should work:
implicitly[Functor[Tree]].map(Branch(Leaf(10), Leaf(20)))(_ * 2)
or simply
Functor[Tree].map(Branch(Leaf(10), Leaf(20)))(_ * 2)
(since scalaz and cats usually have this convenience apply() for type classes)
EDIT: When reading your question, I missed the Functor[Tree].map(b)((v) => v * 4) part. So what you're actually interested in is the following part.
If you want to keep your syntax for invoking map, you can create an implicit class as a wrapper:
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)
As pointed out by @P. Frolov, you can get this implicit conversion for free from import scalaz.syntax.functor._ or import cats.syntax.functor._, but note that you have to explicitly upcast your Branch to be an instance of Tree.
Since you're using Cats, you can import cats.syntax.functor._ in order to get your map. Note that you need to convert branch into the base type, because Functor instance is defined for Tree, not just a separate Branch. You can use type annotation or smart constructors in companion object to do so.
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 is actually just a singleton object that extends a FunctorSyntax trait, packing an implicit conversion from the class that implements Functor to the wrapper like one in slouc's answer. Wrapper itself, Functor.Ops, is generated by a Simulacrum @typeclass macro.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With