1. 型クラス中級講座

型クラス中級講座 

Haskell の文法に関しては飛ばして第8章の型や型クラスを自分で作ろう まで行こう (本を持っている人は第7章)。

信号の型クラス 

data TrafficLight = Red | Yellow | Green

これを Scala で書くと:

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
}

これに Eq のインスタンスを定義する。

implicit val trafficLightEq: Eq[TrafficLight] =
  new Eq[TrafficLight] {
    def eqv(a1: TrafficLight, a2: TrafficLight): Boolean = a1 == a2
  }
// trafficLightEq: Eq[TrafficLight] = repl.MdocSession1@7eb2adb6

注意: 最新の algebra.Equal には Equal.instanceEqual.fromUniversalEquals も定義されている。

Eq を使えるかな?

TrafficLight.Red === TrafficLight.Yellow
// error: value === is not a member of object repl.MdocSession.App.TrafficLight.Red
//   TrafficLight.red === TrafficLight.yellow
//   ^^^^^^^^^^^^^^^^^^^^

Eq が不変 (invariant) なサブタイプ Eq[A] を持つせいで、Eq[TrafficLight] が検知されないみたいだ。 この問題を回避する方法としては、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

ちょっと冗長だけども、一応動いた。