I need to pass two functions as parameters to a scala function. That function should then evaluate them and get a number from them where it will then operate on. This number can be either a Int, Double or any other numeric type. I would like the function to work, whatever the types it is working with.
The example bellow explains the issue.
import Numeric.Implicits._
class Arithmetic[T : Numeric](val A: Connector[T], val B: Connector[T]) {
val sum = new Connector({ A.value + B.value })
}
class Constant[T](var x: T) {
val value = new Connector({ x })
}
class Connector[T](f: => T) {
def value: T = f
override def toString = value.toString()
}
object Main extends App{
val n1 = new Constant(1)
// works
val n5 = new Constant(5)
val a = new Arithmetic( n1.value, n5.value )
println(a.sum)
// no works
val n55 = new Constant(5.5)
val b = new Arithmetic( n1.value, n55.value )
println(b.sum)
}
I've also tried
class Arithmetic[T,R : Numeric](val A: Connector[T], val B: Connector[R]) {
and several other combinations, but I ended up with
error: could not find implicit value for parameter num: scala.math.Numeric[Any]
val sum = new Connector({ A.value + B.value })
The error message you are seeing is because Numeric[T].plus
can only be used to add two values of the same type T
.
Your code is written under the assumption that numeric widening happens automatically - which will not in this case as the compiler does not know anything about the types except that there exists a Numeric[T]
instance.
If you need sum
to be a stable value, you will have to provide the necessary type information in the constructor like this:
class Arithmetic[A : Numeric, R <% A, S <% A](val a: Connector[R], b: Connector[S]) {
val sum = new Connector[A]((a.value:A) + (b.value:A))
}
This requires types R
and S
to be convertible into some type A
for which a Numeric[A]
istance is known.
When creating an instance you would always have to provide all type parameters as they cannot be inferred.
If you do not need sum
to be stable you could change your class to this:
class Arithmetic[A,B](val a: Connector[A], val b: Connector[B]) {
// if A and B are the same types
def sum(implicit e: B =:= A, n: Numeric[A]): Connector[A] =
new Connector(n.plus(a.value, b.value))
// else widen to C
def wideSum[C](implicit f: A => C, g: B => C, n: Numeric[C]) =
new Connector(n.plus(a.value, b.value))
}
val a = new Connector(1)
val b = new Connector(2)
val c = new Connector(3.0)
val d = (new Arithmetic(a,b)).sum
// val e = (new Arithmetic(b,c)).sum // <-- does not compile
val e = (new Arithmetic(b,c)).wideSum[Double]
When widening you will still have to provide the type information though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With