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

+3


source to share


3 answers


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

      

+3


source


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)

      

+3


source


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"

      

+3


source







All Articles