Scala implicit numeric [T] in companion object

I have the following general Interval class (kindly formulated for me by 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 the binary operations of union and intersection, I performed a similar pattern with (implicit num: Numeric[T]) in a 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) } } 

This is an ugly template for copying (implicit num: Numeric[T]) and import num.mkOrderingOps inside both methods. Is there a way to do this only once, at the level of the Interval object itself?

+6
source share
2 answers

Yes there is.

Import first. Instead, you can import Ordering.Implicits._ into the Interval area.

 object Interval { import Ordering.Implicits._ def union[T](....)(implicit num: Numeric[T]) = { // do not import num.mkOrderingOps ... } ... } 

With these implications, when he finds an ordering operation, he will look for implicit ordering (a numerical order value) in the area where the operation takes place. And in each of your routines, there is one proper hidden area. If you need arithmetic operations, also import Numeric.Implicits._

Now with an implicit argument. To do this, there is a shortcut in the language called the context boundary: you can write def f[T: X](args) , and not def f[T](args)(implicit someName: X[T])

The difference is that you don’t have a name for the implicit with context binding (you can use implictly [T], but then this is hardly shorter. Fortunately, you no longer need the name with Import Ordering. Implicits._

So

 object Interval { import Ordering.Implicits._ // also import Numeric.Implicits._ if you need +,-,*,/ ... def union[T: Numeric] ... def intersection[T: Numeric] ... } 
+8
source

Using a class of type Numeric in an Interval object has a parameter of type T , which must be bound somewhere in its scope. Interval , being a unique constant value, cannot provide this binding.

One solution to this particular problem would be to move the definitions of your union and intersect operations to the Interval class as normal instance methods, in which case they would share the T binding and the associated Numeric instance with the rest of the class.

 case class Interval[T : Numeric](from: T, to: T) { import Numeric.Implicits._ import Ordering.Implicits._ def mid: Double = (from.toDouble + to.toDouble) / 2.0 def union(interval2: Interval[T]) = Interval(this.from min interval2.from, this.to max interval2.to) def intersect(interval2: Interval[T]) = Interval(this.from max interval2.from, this.to min interval2.to) } 

If, however, you prefer to keep the definitions of these operations separate from the Interval class, one approach to reducing the number of implicit templates that you need to pursue through your APIs is to define your own type of higher-level classes in terms of Numeric [T]. For instance,

 // Type class supplying union and intersection operations for values // of type Interval[T] class IntervalOps[T : Numeric] { import Ordering.Implicits._ def union(interval1: Interval[T], interval2: Interval[T]) = Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) def intersect(interval1: Interval[T], interval2: Interval[T]) = Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) } implicit def mkIntervalOps[T : Numeric] = new IntervalOps[T] 

to be used

 def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : IntervalOps[T]) = { import ops._ val i3 = union(i1, i2) val i4 = intersect(i1, i2) (i3, i4) } 

The third option combines the two using an implicit definition to enrich the original class with additional methods,

 class IntervalOps[T : Numeric](interval1 : Interval[T]) { import Ordering.Implicits._ def union(interval2: Interval[T]) = Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) def intersect(interval2: Interval[T]) = Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) } implicit def enrichInterval[T : Numeric](interval1 : Interval[T]) = new IntervalOps[T](interval1) type Ops[T] = Interval[T] => IntervalOps[T] 

Then when using

 def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : Ops[T]) = { val i3 = i1 union i2 val i4 = i1 intersect i2 (i3, i4) } 
+5
source

Source: https://habr.com/ru/post/915591/


All Articles