型クラス中級講座 

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

信号の型クラス 

data TrafficLight = Red | Yellow | Green

これを Scala で書くと:

scala> import cats._, cats.data._, cats.implicits._
import cats._
import cats.data._
import cats.implicits._

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

これに 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@6f059f59

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

Eq を使えるかな?

scala> TrafficLight.Red === TrafficLight.Yellow
<console>:26: error: value === is not a member of object TrafficLight.Red
       TrafficLight.Red === TrafficLight.Yellow
                        ^

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

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

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

Contents