I have the following generic Interval class (kindly formulated for me by user soc):
case class Interval[T](from: T, to: T)(implicit num: Numeric[T]) {
import num.mkNumericOps // allows us to write from.toDouble and to.toDouble
def mid: Double = (from.toDouble + to.toDouble) / 2.0
}
Typical use cases: Interval[Double] or Interval[Int]. To add binary union and intersection operators I followed a similar pattern with (implicit num: Numeric[T])
in the companion object:
object Interval {
def union[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = {
import num.mkOrderingOps // allows interval1.from min
Interval[T](interval1.from min interval2.from, interval1.to max interval2.to)
}
def intersect[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = {
import num.mkOrderingOps
Interval[T](interval1.from max interval2.from, interval1.to min interval2.to)
}
}
It's ugly boilerplate to copy the (implicit num: Numeric[T])
and import num.mkOrderingOps
inside both methods. Is there some way to do this just once, at the level of the Interval object itself?
Yes there is.
First with the import. You can import Ordering.Implicits._ in the scope of Interval instead.
object Interval {
import Ordering.Implicits._
def union[T](....)(implicit num: Numeric[T]) = {
// do not import num.mkOrderingOps
...
}
...
}
With those implicits, when it find an ordering operation, it will look for the implicit Ordering (Numeric is an Ordering) in scope where the operation happens. And there happens to be one proper implicit in scope in each of your routines. If you need arithmetic operations too, also import Numeric.Implicits._
Now with the implicit argument. There is a shortcut in the language for that, which is called a context bound :
you can write def f[T: X](args)
rather than def f[T](args)(implicit someName: X[T])
The difference is that you don't have a name for the implicit with the context bound (you can use implictly[T] but then this is hardly shorter. Fortunately, you do not need a name anymore with the import Ordering.Implicits._
So
object Interval {
import Ordering.Implicits._
// also import Numeric.Implicits._ if you need +,-,*,/ ...
def union[T: Numeric] ...
def intersection[T: Numeric] ...
}