Search code examples
scalaimplicit-conversionscala-3

Scala 3 implicit conversions: compare value and literal


I have an opaque type FancyDouble, which is implicitly converted from Double by using given Conversion[Double, FancyDouble] = FancyDouble(_) in the companion object scope. After that, the construction val d: FancyDouble = 0.0 works, but when I try to do the comparison like if (d == 0.0), the compiler is complaining that I can't compare FanceDouble and Double (I am expecting that it should implicitly convert the 0.0 literal into FancyDouble, like in the previous case.

How can I enable the comparison with implicit conversions?


Solution

  • Equality is essentially defined as (Any, Any) => Boolean. Mostly because of Java legacy. That means that in fancyDouble == double the expected type of expression double is Any and no implicit conversion will ever trigger.

    However Scala 3 introduced the CanEqual typeclass to make equality a bit safer. If you want to compare values of type A and B, an implicit instance of CanEqual[A, B] needs to be in the implicit scope.

    object fancy {
      opaque type FancyDouble = Double
      def make(d: Double): FancyDouble = d
    
      given CanEqual[FancyDouble, Double] = CanEqual.derived
      given CanEqual[Double, FancyDouble] = CanEqual.derived
    }
    
    val a = 4.2
    val b = fancy.make(4.2)
    
    assert(a == b) // ok
    assert(b == a) // ok
    

    If you enable the strictEquality flag, you even need a CanEqual[FancyDouble, FancyDouble] in order to compare two FancyDoubles.