Search code examples
scalaimplicit

How to debug and fix Diverging implicits


I'm a beginner to scala and I'm trying to wrap my head around diverging implicit expansions.

Here's my toy code:


class FooList[T](implicit ord: Ordering[T]) {

}

class Human(val score: Int) extends Ordering[Human] {


  override def compare(x: Human, y: Human): Int = {
    return x.score.compareTo(y.score);  
  }
}


object Main {

  def main(args: Array[String]): Unit = {
    val h = new Human(100)
    val lst = new FooList[Human]();
  }
}

I'm getting an error:

diverging implicit expansion for type Ordering[Human]
starting with method comparatorToOrdering in trait LowPriorityOrderingImplicits


not enough arguments for constructor FooList: (implicit ord: Ordering[Human])FooList[Human].
Unspecified value parameter ord.

https://scastie.scala-lang.org/wkSrZ6BMQKW9ZJrsZhlITQ

I'd love a pointer or two in the right direction :)


Solution

  • The "diverging" message is a bit confusing here, the real problem is a misunderstanding about how type classes work. This is what it should look like:

    class FooList[T](implicit ord: Ordering[T]) {
    }
    
    case class Human(score: Int)
    
    implicit object HumanOrdering extends Ordering[Human] {
      def compare(x: Human, y: Human): Int =
        x.score.compareTo(y.score)
    }
    
    object Main {
      def main(args: Array[String]): Unit = {
        val h = new Human(100)
        val lst = new FooList[Human]()
      }
    }
    

    Note that the Human class is just a simple data type. There is a separate implicit object that implements ordering for Human objects by extending Ordering[Human].

    The main point about a typeclass is that the behaviour is not part of the original class but is bound to it by creating an implicit value of the appropriate type. This allows you to add multiple behaviours to classes without having to modify the underlying class itself.


    Note that you can put this implicit object inside the class object if you want to keep things a bit cleaner:

    object Human {
      implicit object HumanOrdering extends Ordering[Human] {
        def compare(x: Human, y: Human): Int =
          x.score.compareTo(y.score)
      }
    }