Search code examples
scalaenumsimplicit

Case object enumerations, order and implicits


For pedagogical purposes I have implemented an enumeration using both Enumeration and case objects. I'm comfortable with the Enumeration version, although it cannot warn of missing cases in a match statement.

I have tried to write the equivalent using case objects but I am finding that, although it compiles, it doesn't run. In particular, it seems to go into an infinite loop at run time (I haven't been able to confirm this in the debugger, however). Here's the code I'm trying to run:

sealed abstract class Suit extends Ordered[Suit] {
  val name: String
  val priority: Int
  override def toString = name
  def compare(that: Suit) = priority-that.priority
}
case object Spades extends Suit { val name = "Spades"; val priority = 0 }
case object Hearts extends Suit { val name = "Hearts"; val priority = 1 }
case object Diamonds extends Suit { val name = "Diamonds"; val priority = 2 }
case object Clubs extends Suit { val name = "Clubs"; val priority = 3 }

object Suit {
  implicit def ordering[A <: Suit]: Ordering[A] = Suit.ordering
  def suits = List(Clubs,Spades).sorted
}

If I enter this code into the REPL (via paste mode) or if I compile it in Eclipse, all appears well. But if I try to test the suits method of the Suit object, the run just hangs and I have to terminate it.

I have tried most if not all of the suggestions I've found here on StackOverflow regarding implicits (for instance, not defining Suit class to extend Ordered[Suit] and instead specifying an Ordering directly for the class. None of these have compiled however.

Working suggestions much appreciated.


Solution

  • object Suit {
      implicit def ordering[A <: Suit]: Ordering[A] = Suit.ordering
    }
    

    That is an infinite loop, ordering calls itself. I think you want an Ordering[Suit], it doesn't make sense in this case to ask for an ordering of a sub-type A of Suit (that would be one individual case object).

    There is already an implicit ordering for types that extend Ordered, so you won't even need to construct one:

    implicitly[Ordering[Suit]]  // aka Ordering.ordered[Suit]
    

    So the following just works:

    object Suit {
      def suits = List[Suit](Clubs, Spades).sorted
    }
    

    Note that List(Clubs, Spades).sorted won't work, because the inferred list element type is Suit with Product and somehow that produces a failure to find an unambiguous ordering.