Writing new class for bounded Double


#1

I have written a custom case class that checks that a Double is in the interval [0,1] as:

 implicit class DoubleIn01(val value: Double) extends Ordered[DoubleIn01] {

  require(inBounds(0.0, value, 1.0), s"value $value should be in [0,1]")

  override def toString: String = value.toString
  override def hashCode(): Int = value.hashCode()
  override def compare(that: DoubleIn01): Int = value.compare(that.value)

  override def equals(obj: Any): Boolean =
    obj match {
      case o: Double => value.equals(o.asInstanceOf[Double])
      case o: DoubleIn01 => value.equals(o.asInstanceOf[DoubleIn01].value)
      case _ => false
    }

  def ==(that: Double): Boolean = value == that
}

And I have added an implicit function:

implicit def DoubleIn01ToDouble(x: DoubleIn01): Double = x.value

But that is not enough to ensure I can write:

assertEquals(DoubleIn01(0.5), 0.5)

(in testng, which checks the equality both ways).

What am I missing?

Note: I tried deriving from AnyVal, but that didn’t allow me to have the require statement in the class, apparently, so AnyVal seems to be out.

This is a bit frustrating, as there doesn’t seem to be a simple way to write such a DoubleIn01 class in Scala. I spent several days on this, and each got bit one way or another by the limitations of AnyVal, or implicit. I really just want to not have to write more than the require statement, and be interoperable with Java’s Double and/or the native double type, but apparently no such luck in Scala? (please prove me wrong :slight_smile:


#2

The reason your implicit def does not work in this case is, that implicit conversions are only applied, if the expression doesn’t match a required type or a method s called on it which it does not have (https://docs.scala-lang.org/tour/implicit-conversions.html)

When assertEquals checks equality via DoubleIn01(0.5).equals(0.5) this works because of your equals definition. But when it then checks the other way round, it calls 0.5.equals(DoubleIn01(0.5)). Because Double has an equals method, the implicit class is not applied and because equals takes a parameter of type Any, the implicit def from your class to double is also not applied, because DoubleIn01 is already a valid type.

I’m not sure if it is possible to get that amount of compatibility to Java doubles. As soon as a method takes Any / Object as parameter, your implicit conversions won’t be called. Your best bet is using type safe equality operators from a library like scalactic but those won’t be used by java libraries like testng of course…


#3

Thanks a lot for this explanation!

It is a bit disappointing: I am fairly new to Scala, but I am getting an impression that there are gotchas/limitations. The explanation you provided makes total sense, but it feels like one Scala feature (implicits) plays nice with type inference … up to a point (there are unavoidable limitations), or possibly, that Scala is pushing me towards a design choice (implement this kind of bound check as a function in a pre-condition, rather than as a type), which I perceive as a limitation too. I love Scala, but I wish the language was “smoother”.