Can't write a method in Scala that works for both Double and Float
I am having a hard time writing a method that can be used with Float
and Double
. The problem is that I need to multiply my generic type parameter A
with Double
inside my method. It boils down to something like this:
def multiplyWithPi[A](in: A)(implicit num: Numeric[A]) : A = {
Math.PI * in // does not compile
num.times(Math.PI, in) // does not compile
num.times(Math.PI.asInstanceOf[A],in) // does not work (class-cast exception)
}
How can i do this? It is important that the return type is the same as the input type
source to share
scala.math.Numeric
provides a method fromInt
, it must also have fromDouble
. This is how it will look:
trait MyNumeric[T] {
def times(a: T, b: T): T
def fromDouble(d: Double): T
}
object MyNumeric {
implicit val MyNumericInstance4Double = new MyNumeric[Double] {
def times(a: Double, b: Double): Double = a * b
def fromDouble(d: Double): Double = b
}
implicit val MyNumericInstance4Float = new MyNumeric[Float] {
def times(a: Float, b: Float): Float = a * b
def fromDouble(d: Double): Float = b.toFloat
}
}
def multiplyWithPi[A](in: A)(implicit num: MyNumeric[A]) : A =
num.times(num.fromDouble(Math.PI), in)
If you think that the class classes are too complex for your needs, you can also go the unsafe way:
def multiplyWithPi[A](in: A)(implicit num: Numeric[A]): A =
(in match {
case i: Float => (i * Math.PI).toFloat
case i: Double => (i * Math.PI).toDouble
}).asInstanceOf[A]
Or even:
def multiplyWithPi(in: Double): Double = in * Math.PI
def multiplyWithPi(in: Float): Float = (in * Math.PI).toFloat
source to share
It won't work because it Math.PI
always Double
and never will Float
. Since Numeric
typeclass does not provide a pi value, you need to add an additional class:
trait Pi[A] { def apply():A }
object Pi {
implicit val double:Pi[Double] = new Pi[Double] { def apply() = Math.PI }
implicit val float:Pi[Float] = new Pi[Float] { def apply() = Math.PI.toFloat }
}
Then you can do the following:
def multiplyWithPi[A](in: A)(implicit num: MyNumeric[A], pi: Pi[A]) : A =
num.times(pi(), in)
source to share
The Numeric[T]
scala built-in class is pretty simple. If you want to do something more advanced with math in scala, I suggest using the spire math library .
This is how you would implement the function in spire:
import spire.algebra._ // algebraic typeclasses
import spire.implicits._ // syntax and instances
def multiplyWithPi[A: Ring: Trig](x: A) : A = {
Trig[A].pi * x
}
Here's how it works: Ring[T]
typeclass is for types that allow multiplication. A class Trig[T]
for types that enable trigonometry. In addition to a trigonometric function such as sin, it also has pi
and values e
.
Note that this approach does not rely on the conversion from the imprecise double approximation to T, which you can find in Math.PI
. For example. spire has several types of numbers for precise arithmetic. For them, the conversion from the double approximation would be very imprecise.
Imports spire.algebra._
are for all types including Trig[T]
and Ring[T]
. The import spire.implicits._
provides predefined typeclass instances for Float
and Double
.
To use spire add the following to your build.sbt:
libraryDependencies += "org.spire-math" %% "spire" % "0.13.0"
source to share