Search code examples
scalacollectionssumoperator-overloadingimplicit

Scala collection sum to work with overloaded addition


I am trying to make an overloaded addition operator work with collections' sum method. For example, for

final case class Probability(val value: Double) {
  def +(that: Probability): Probability =
    Probability(this.value + that.value)
}

List(Probability(0.4), Probability(0.3)).sum

I get the below error messages:

Error:(6, 109) could not find implicit value for parameter num:
  Numeric[A$A42.this.Probability]
def get$$instance$$res0 = /* ###worksheet### generated $$end$$ */
  List(Probability(0.4), Probability(0.3)).sum;}

Error:(6, 109) not enough arguments for method sum: 
  (implicit num: Numeric[A$A42.this.Probability])A$A42.this.Probability.
Unspecified value parameter num.
def get$$instance$$res0 = /* ###worksheet### generated $$end$$ */
   List(Probability(0.4), Probability(0.3)).sum;}

I reckon this has to do something with implicits (as the error message clearly states), but I don't know where it should be defined or added (in the case class Probability, its companion object, or after sum?), or how it should look.


Solution

  • Sum method works with the type class Numeric[T], so if you want to call sum over your own "number type", you need to define an instance of Numeric[Probability].

    The best place to put a custom type class definition is the object companion.

    object Probability {
    
      implicit val probabilityNumberic = new Numeric[Probability] {
        def plus(x: Probability, y: Probability): Probability = ???
        def minus(x: Probability, y: Probability): Probability = ???
        def times(x: Probability, y: Probability): Probability = ???
        def negate(x: Probability): Probability = ???
        def fromInt(x: Int): Probability = ???
        def toInt(x: Probability): Int = ???
        def toLong(x: Probability): Long = ???
        def toFloat(x: Probability): Float = ???
        def toDouble(x: Probability): Double = ???
        def compare(x: Probability, y: Probability): Int = ???
      }
    
    }
    

    Alternatively if you dont want to define the Numeric[T] instance or if you want a Probability as result, you can use reduce instead of sum.

    List(Probability(0.4), Probability(0.3)).reduce(_ + _)
    

    or reduceOption if the list could be empty.

    About where you can define your Numeric[Probability],you must take into account implicits's scope.It is common to define a default implementation on the object companion, but you can create it before sum call, through an import, or you can use it explicitly. It depends on what you need.