Type to impose required constrains on a double

124 views Asked by At

I would like to have a run time check on a Double, without having to scatter the check all over my code. I thought that defining an implicit class would do the job, something on the line:

  implicit class Probability(val x: Double) {
    require(x >= 0.0 && x <= 1.0, "Probs are defined btw 0.0 and 1.0")
  }

The missing part, is then to tell Scala to treat Probability as a Double any time after it's construction. For this I suppose it is necessary to require a double side conversion.

object Foo {

  implicit class Probability(val x: Double) {
    require(x >= 0.0 && x <= 1.0, "Probs are defined btw 0.0 and 1.0")
  }

  implicit def asDouble(e: Probability): Double = e.x

  implicit def asProb(d: Double): Probability = new Probability(d)

  def max_(s: Seq[Double]): Double = {
    s.max
  }

  def max(s: Seq[Probability]): Double = {
    s.max
  }
}

val r1 = Foo.max_(List(2.0, 3.0, 5.0))
val r2 = Foo.max(List[Probability]=(2.0, 3.0, 5.0))

EDIT

This might have done the trick. Not sure what happens under the hood.

trait RangeBound
type Probability = Double with RangeBound
implicit def makeProb(p: Double): Probability = {
  assert (p >= 0.0 && p <= 1.0)
  p.asInstanceOf[Probability]
}

val p = List[Probability](0.1, 0.3, 0.2)
val r = p filter (_ > 0.1)

Because this does not work:

trait RangeBound
type Probability = Double with RangeBound
implicit def makeProb(p: Double): Probability = {
  assert (p >= 0.0 && p <= 1.0)
  p.asInstanceOf[Probability]
}

val p = List[Probability](0.1, 0.3, 0.2)
val r2 = p.max

with the error:

Error:(10, 18) No implicit Ordering defined for Double with A$A288.this.RangeBound.
lazy val r2 = p.max;}
                ^
2

There are 2 answers

0
NoIdeaHowToFixThis On BEST ANSWER

This enabled basic run time checks:

trait RangeBound
type Probability = Double with RangeBound

  implicit def makeProb(p: Double): Probability = {
    assert (p >= 0.0 && p <= 1.0)
    p.asInstanceOf[Probability]
  }

  implicit val probabilityOrdering = Ordering.Double.asInstanceOf[Ordering[Probability]]
2
volia17 On

I don't understand what you want.

But, in case, here a little proposition to you.

  class Probability(val x: Double) extends AnyVal {
    override def toString = s"${x * 100} %"
    def PerCent = x * 100
  }

  object Probability {
    def apply(x: Double) = {
      if (x <= 1.0 && x >= 0.0)
        Some(new Probability(x))
      else
        None
    }
  }

  implicit def asProb(d: Double): Probability = Probability(d).get

The implicit conversion should only be called when a method or value is used, like PerCent in my example :

scala> 0.3
res0: Double = 0.3

scala> 0.3.PerCent
res1: Double = 30.0

scala> 1.5
res2: Double = 1.5

scala> 1.5.PerCent
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  at .asProb(<console>:21)
  ... 43 elided