14
scala> class Foo[T <: Comparable[T]](val x : T)
defined class Foo

scala> (3: Int).asInstanceOf[Comparable[Int]]  
res60: java.lang.Comparable[Int] = 3

scala> new Foo(3)                              
<console>:13: error: inferred type arguments [Int] do not conform to class Foo's type parameter bounds [T <: java.lang.Comparable[T]]
       new Foo(3)
       ^

Is the 2nd expression the result of type erasure?

How would I go about defining Foo so that I could parameterize it with Int but still be able to perform some ordering behavior with its instance variable?

Collin
  • 1,431
  • 12
  • 19
  • 1
    It's easy to forget (coming from Java) that types like Int and Double are primitives and therefore cannot conform to reference types like Number and Comparable. – Aaron Novstrup Nov 06 '10 at 21:04

3 Answers3

17

Use a view bound.

Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class Foo[T <% Comparable[T]](val x : T)
defined class Foo

scala> new Foo(3)
res0: Foo[Int] = Foo@9aca82
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • 2
    Thanks for the link. That's super helpful and I've been wondering what the <% meant for a while. So if I understand it correctly, T <% Comparable[T] where T is Int, ensures that there is an implicit conversion from Int to Comparable[Int], and in this case I guess it picks that up from Predef.intWrapper which converts to a RichInt which is Comparable[Int]. I understand it now. I do wish that Scala had a 'debug' mode that would show where / how some of these implicit conversions take place. – Collin Nov 06 '10 at 14:30
  • 5
    @Collin: `scalac -Xprint:typer myprogram.scala` will show you where implicits got used. Type `scalac -Xshow-phases` to see what other phases there are. – Daniel C. Sobral Nov 06 '10 at 21:13
16

The question, as stated, is still unanswered (though "use view bounds" solves the problem, which is more useful). The answer is simply that an Int in Scala is supposed to be equivalent to an int in Java, which is not a class at all, and, therefore, cannot even be a Comparable (though that could be solved in Java 7 with defender methods... I wonder if they'll do it).

The solution given, to use a view bound, is used throughout Scala to solve the problem of a class that could implement something but doesn't, because it is not under Scala's control -- ie, Java classes.

And, of course, it can be used by programmers themselves to deal with similar stuff from libraries and frameworks, or simply to produce wrappers around a library to give it a Scala-ish feeling.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
7

Alternatively, you could use a context bound:

class Foo[T: Ordering](val v: T)

or

class Foo[T: java.util.Comparator](val v: T)

The context bound represents an assertion that there is an implicit Ordering[T] (or java.util.Comparator[T]) in scope when the constructor is invoked and is equivalent to adding an implicit parameter:

class Foo[T](val v: T)(implicit ev: Ordering[T])

The advantage of this approach is that it will allow you to use an alternate ordering based on the context:

// by default, new Foo("a", "c", "b").items == List("a", "b", "c")
class Foo[T: Ordering](xs: T*) {
   val items = xs.toList.sorted
}

// with this object in scope, 
// new Foo("a", "c", "b").items == List("c", "b", "a")
implicit val descending = Ordering[String].reverse
Community
  • 1
  • 1
Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
  • I had not understood context bounds until now, and after reading your answer I went off and read further. Very useful and informative. Thanks. – Collin Nov 07 '10 at 17:45