views:

285

answers:

1

The scala docs say that Enumeration.Val is ordered, however I get inconsistent behavior when when I try to enforce type restrictions on enumeration values requiring them to support ordering:

object Dogs extends Enumeration {
    val Sam, Tom, Rover = Value
}

def doSomething[A <% Ordered[A]](a : List[A]) : Unit = {
    println(a.sortWith(_ < _))
}

import Dogs._

val xs = List(Rover, Tom, Sam, Sam, Rover)

println(xs.sortWith(_ < _))  // works!
doSomething(xs)              // fails =(

Of the last two statements, the first works and shows that Enumeration values have an ordering defined. The second gives an error:

could not find implicit value for evidence parameter of type (this.Dogs.Value) => Ordered[this.Dogs.Value]

How do I get around this and use enumeration values in generic methods which require ordering?

+6  A: 

The problem is that Value implemenents Ordered[Enumeration#Value], rather than Ordered[Dogs.Value]. I don't know the rationale for this, it not be possible to do it the other way.

This is sufficient for the simple case of directly comparing two values -- its just a normal method call:

scala> (Rover: Ordered[Enumeration#Value]).<(Sam)
res44: Boolean = false

However, the type parameter A in Ordered is invariant, so when you you ask for type param to viewable as Ordered[A], it is not sufficient to use Dogs.Value <: Ordered[Enumeration#Value]. If A were contra-variant this would be allowed, but it also causes other problems with type inference.

You could work around the problem by statically typing the list with Enumeration#Value:

scala> val xs = List[Enumeration#Value](Rover, Tom, Sam, Sam, Rover)
xs: List[Enumeration#Value] = List(Rover, Tom, Sam, Sam, Rover)

scala> doSomething(xs)                 
List(Sam, Sam, Tom, Rover, Rover)

Or, by explicitly passing the type parameter to doSomething:

scala> doSomething[Enumeration#Value](List(Rover, Sam))                          
List(Sam, Rover)

Or, better yet, loosening the requirements on the type parameters, essentially treating Ordered as contravariant in this circumstance.

scala> def doSomething[A <% Ordered[_ >: A]](xs : List[A]) = xs sortWith (_ < _)
doSomething: [A](xs: List[A])(implicit evidence$1: (A) => Ordered[_ >: A])List[A]

scala> doSomething(List(Rover, Sam))                                            
res47: List[Dogs.Value] = List(Sam, Rover)

Why does that work?

scala> Rover: Ordered[_ <: Enumeration#Value]
res52: scala.math.Ordered[_ <: Enumeration#Value] = Rover
retronym
Thanks @retronym, this last example solved my problem, though my mind is bending from the weaker type constraints!It now requires any type A, provided A can be treated as (i.e. is, or can be converted to) something Ordered by a generalization of A. Is that about right?
Lars Yencken
Pretty much!You can check type relations between any two types with some clever types in Predef (2.8). Conformance: `implicitly[A <:< B]`, equivalence: `implicitly[A =:= B]`, or implicit viewability: `implicitly[A <%< B]`. Try these:`implicitly[Ordered[Int] <:< Ordered[_ <: Int]]``implicitly[Ordered[_ <: Int] =:= (Ordered[T] forSome { type T <: Int })]`
retronym