简体   繁体   中英

Generic Type `toDouble` in Scala

Hi have the following as bellow. It is supposed to be a function that calculate the average of a sequence.

 def fun1[T <: { def toDouble:Double }] (seq:Seq[T]) = {
    val q2 = seq map { _.toDouble }
    val n = q2.size.toDouble
    q2.sum / n
  }

However I cannot pass Seq(1,2,3) to fun1 . Why?

Structural types are very expensive on the JVM and you should avoid them. It stands to reason that only a Numeric could be encoded as a Double . Scala offers a Numeric typeclass as a way to define generic operations on the so called "number types", so you don't actually need to use what you are proposing, the standard lib has you covered.

def fun1[T](seq: Seq[T])(ev: Numeric[T]): Double = {
  val q2 = seq map ev.toDouble
  val n = q2.size.toDouble
  q2.sum / n
}

You don't really need to get more creative than that if all you want is your operation to be generic on numbers. Otherwise, you will have to implement your own typeclass.

trait Converter[T] {
  def toDouble(source: T): Double
}
object Converter {
  implicit def numericConvert[T : Numeric]: Converter[T] = new Converter[T] { def toDouble(source: T): Double = implicitly[Numeric[T]].toDouble(source)}
  implicit object StringConverter extends Converter[String] = ...
} 

Now to use the simple approach based on Numeric , you could define a simple helper.

object Helper {
  implicit class ColOps[
    M[X] <: TraversableOnce[X],
    T
  ](val col: M[T])(implicit ev: Numeric[T]) {
    def average: Double = {
      val q2 = col map ev.toDouble
      val n = q2.size.toDouble
      q2.sum / n
    }
  }
}

Now all you need to do is:

   import Helper._

   List(1, 2, 3).average
   Seq(1, 2, 3).average
   Set(5F, 2F, 3F).average

This is a bit more cool, since we can abstract over common collection types too.

def fun1[T <: AnyVal{ def toDouble:Double }] (seq:Seq[T]) = {
  val q2 = seq map { _.toDouble }
  val n = q2.size.toDouble
  q2.sum / n
}

fun1(Seq(1, 2, 3))

It seems like the compiler infers the type as AnyRef. Defining it as AnyVal works. AnyRef is the supertype of all objects, while AnyVal is the supertype of all primitives.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM