I am now going to skip over to Chapter 8 Making Our Own Types and Typeclasses (Chapter 7 if you have the book) since the chapters in between are mostly about Haskell syntax.
data TrafficLight = Red | Yellow | Green
In Scala this would be:
import cats._, cats.syntax.all._
sealed trait TrafficLight
object TrafficLight {
case object Red extends TrafficLight
case object Yellow extends TrafficLight
case object Green extends TrafficLight
}
Now let’s define an instance for Eq.
implicit val trafficLightEq: Eq[TrafficLight] =
new Eq[TrafficLight] {
def eqv(a1: TrafficLight, a2: TrafficLight): Boolean = a1 == a2
}
// trafficLightEq: Eq[TrafficLight] = repl.MdocSession1@124d506d
Note: The latest algebra.Equal includes Equal.instance and Equal.fromUniversalEquals.
Can I use the Eq?
TrafficLight.Red === TrafficLight.Yellow
// error: value === is not a member of object repl.MdocSession.App.TrafficLight.Red
// TrafficLight.red === TrafficLight.yellow
// ^^^^^^^^^^^^^^^^^^^^
So apparently Eq[TrafficLight] doesn’t get picked up because Eq has nonvariant subtyping: Eq[A].
One way to workaround this issue is to define helper functions to cast them up to TrafficLight:
import cats._, cats.syntax.all._
sealed trait TrafficLight
object TrafficLight {
def red: TrafficLight = Red
def yellow: TrafficLight = Yellow
def green: TrafficLight = Green
case object Red extends TrafficLight
case object Yellow extends TrafficLight
case object Green extends TrafficLight
}
{
implicit val trafficLightEq: Eq[TrafficLight] =
new Eq[TrafficLight] {
def eqv(a1: TrafficLight, a2: TrafficLight): Boolean = a1 == a2
}
TrafficLight.red === TrafficLight.yellow
}
// res2: Boolean = false
It is a bit of boilerplate, but it works.