scala/basic

sealed trait typeclass

wefree 2022. 3. 29. 14:45

문제

sealed trait 대상으로 typeclass 를 정의할 때, 다음과 같은 문제가 있다.

https://www.reddit.com/r/scala/comments/96woxd/how_to_implement_an_adt_that_all_members_have/

 

코드

http://eed3si9n.com/herding-cats/typeclasses-102.html 참고

trait Eq[A] {
  def eqv(x: A, y: A): Boolean
}

object Eq {
  def apply[A](implicit eq: Eq[A]): Eq[A] = eq

  implicit class EqSyntax[A](x: A) {
    def ===(y: A)(implicit eq: Eq[A]): Boolean = eq.eqv(x, y)
  }
}

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
}

object MyTest {
  def main(args: Array[String]): Unit = {
    implicit val trafficLightEq: Eq[TrafficLight] = new Eq[TrafficLight] {
      override def eqv(x: TrafficLight, y: TrafficLight): Boolean = x == y
    }

    import Eq._
    TrafficLight.Red === TrafficLight.Yellow // ERROR !!!
    TrafficLight.red === TrafficLight.yellow // OK
  }
}

 

설명

TrafficLight.Red === TrafficLight.Yellow 일 때, ERROR 인 것은 implicit Eq[TrafficLight] 가 정의되었지만, implicit Eq[TrafficLight.Red] 는 정의되지 않은 상태이기 때문이다. 그래서 TrafficeLight.Red 를 TrafficLight 타입으로 처리할 수 있도록 def red: TrafficLight = Red 로 helper function 을 정의해 사용했다. http://eed3si9n.com/herding-cats/typeclasses-102.html 에도 잘 설명이 되어있다. 이런 번거로움 때문인지 zio-prelude Equal 에서는 contravariant 타입인 Equal[-A] 로 정의했다.