Home > Software engineering >  apply modulus operator to Scala generic type class member?
apply modulus operator to Scala generic type class member?

Time:05-22

So i have the followig class:

enter image description here

class MyClass[T] {
var data: T = _

def isEven: Boolean = {
    if (this.data % 2 == 0) return true
    false
}

}

Scala isn't allowing me to take modulus here as there is no guarantee that type "T" will be numerical.

So my question is how do i run this? I want to allow MyClass to have any type of numeric dataTypes like Int, Float, Double etc

CodePudding user response:

There's a type class called Numeric, located in package scala.math. This class provides some functionalities, which numerics (like Int, Double, ...) must have, for instance, they can be negated, or they must be able to get converted to Int, and many others, See the docs here.
So I suggest you do this:

import scala.math.Numeric

class MyClass[T : Numeric] {
  var data: T = _
  private final val num: Numeric[T] = implicitly

  def isEven: Boolean = num.toDouble(data) % 2 == 0
}

Scala has already implemented Numeric for some types such as Int, BigInt, Char, BigDecimal, etc,. So if you use one of these standard types, you'll be just fine, but if you plan to use some type that you have defined yourself, consider implementing Numeric[YourType].
Also, try not to use var.

CodePudding user response:

Well, if you want your data to be a number, you should just say so:

    class MyClass(val data: Number) { // DO NOT use `var`.
       def isEven = data.longValue % 2 == 0 // This may not actually do what you want for floating point numbers
     }

Alternatively, if you want to preserve the type of original data for some reason, you can still have a type parameter, but constrain it with a type boundary:

    class MyClass[T <: Number](val data: T) { // T is a subtype of Number
       def isEven = data.longValue % 2 == 0
    }

This is almost the same as above, except it lets you do things like val i: Int = myClass.data with instances of MyClass[Int] but not with, say , MyClass[Long], so you have some added type safety here.

The implicit "type class" trick mentioned in the other answer is more useful for a scenario, where only some methods of your class need the type to be numeric:

     class MyClass[T](val data: T) { 
       def isEven(implicit ev: Numeric[T]) = ev.toLong(data) % 2 == 0
       def asString: = data.toString
     }

Here you can do MyClass(100).isEven() but not MyClass("foo").isEven(), but still can MyClass("foo").asString. One real-life example of how this can be useful is TraversableOnce.toMap function: if you have a collection of pairs, you can easily turn it into a map with seq.toMap, but if the type of the element was anything other than a two-tuple, that call would give you a compilation error.

  • Related