Reputation: 27373
I'm having trouble to write a method which can be used with Float
and Double
. The problem is that I need to multiply my generic type parameter A
with a 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 that? It's important that return type is the same as the input type
Upvotes: 3
Views: 191
Reputation: 12565
The Numeric[T]
typeclass that is built into scala is pretty basic. If you want to do something a bit more advanced with math in scala, I suggest using the spire math library.
Here 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 is how it works: The Ring[T]
typeclass is for types that allow multiplication. The Trig[T]
typeclass is for types that allow trigonometry. In addition to trigonometric function such as sin, it also comes with values pi
and e
.
Note that this approach does not rely on converting from the imprecise double approximation to T you can find in Math.PI
. E.g. spire has several number types for exact arithmetic. For those, converting from a double approximation would be very imprecise.
The spire.algebra._
import is for all typeclasses, including Trig[T]
and Ring[T]
. The spire.implicits._
import 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"
Upvotes: 3
Reputation: 638
This won't work because Math.PI
is always a Double
and never a Float
. Since the Numeric
typeclass doesn't supply a value of pi you'll need to add an additional typeclass:
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)
Upvotes: 3
Reputation: 7768
scala.math.Numeric
provides a fromInt
method, it should arguably also have fromDouble
. Here is how it would look like:
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 feel that type classes are overkill for your needs, you can also go the unsafe route:
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
Upvotes: 3