typeclasses 102 

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.

A traffic light datatype 

data TrafficLight = Red | Yellow | Green

In Scala this would be:

scala> import cats._, cats.instances.all._
import cats._
import cats.instances.all._

scala> :paste
// Entering paste mode (ctrl-D to finish)
sealed trait TrafficLight
object TrafficLight {
  case object Red extends TrafficLight
  case object Yellow extends TrafficLight
  case object Green extends TrafficLight
}

// Exiting paste mode, now interpreting.

defined trait TrafficLight
defined object TrafficLight

Now let’s define an instance for Eq.

scala> implicit val trafficLightEq: Eq[TrafficLight] =
  new Eq[TrafficLight] {
    def eqv(a1: TrafficLight, a2: TrafficLight): Boolean = a1 == a2
  }
trafficLightEq: cats.Eq[TrafficLight] = $anon$1@1eff7ecd

Note: The latest algebra.Equal includes Equal.instance and Equal.fromUniversalEquals.

Can I use the Eq?

scala> import cats.syntax.eq._
import cats.syntax.eq._

scala> TrafficLight.Red === TrafficLight.Yellow
<console>:26: error: value === is not a member of object 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:

scala> :paste
// Entering paste mode (ctrl-D to finish)
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
}

// Exiting paste mode, now interpreting.

defined trait TrafficLight
defined object TrafficLight

scala> implicit val trafficLightEq: Eq[TrafficLight] =
  new Eq[TrafficLight] {
    def eqv(a1: TrafficLight, a2: TrafficLight): Boolean = a1 == a2
  }
trafficLightEq: cats.Eq[TrafficLight] = $anon$1@79df5bb7

scala> TrafficLight.red === TrafficLight.yellow
res1: Boolean = false

It is a bit of boilerplate, but it works.