package com.zibaldone.cats
package ch_01
import cats.Functor
import cats.syntax.functor.*
extension [F[_]: Functor as functor, A](container: F[A])
// ex. use extension method
def mapContainer[B](f: A => B): F[B] = container.map(f)
// a.k.a the covariant functor
trait `functor`[F[_]] extends ch_04.`invariant`[F]:
def map[A, B](fa: F[A])(f: A => B): F[B]
override def imap[A, B](fa: F[A])(forth: A => B)(back: B => A): F[B] = map(fa)(forth)
// ex. define functor for binary tree
enum Tree[+T]:
case Leaf(value: T)
case Branch(left: Tree[T], value: T, right: Tree[T])
object Tree:
// smart constructors are needed to solve the covariance problem in cats
def leaf[T](value: T): Tree[T] = Leaf(value)
def branch[T](left: Tree[T], value: T, right: Tree[T]): Tree[T] = Branch(left, value, right)
given Functor[Tree] = new Functor[Tree]:
override def map[A, B](tree: Tree[A])(f: A => B): Tree[B] = tree match
case Leaf(value) => Leaf(f(value))
case Branch(left, value, right) => Branch(map(left)(f), f(value), map(right)(f))