12

I've got a trait that looks like this (some further information can be found at this related question by myself although I don't think, it's needed for this question)

trait Extractor[-A,+B] {
  def extract(d:A):B
  //lots of other things
}

To use this in an existing java framework I would like this Extractor to either have a function that returns a Comparator[B] (being java.util.Comparator) or even better extend Comparator[A]. Now that poses a problem because Comparators type parameter is ought to be invariant, while A is contravariant and B is covariant.

So I get errors like this:

scala> import java.util.Comparator
import java.util.Comparator

scala> trait Extractor[-A,+B] extends Comparator[A]
<console>:6: error: contravariant type A occurs in invariant position in type [-A,+B]java.lang.Object with java.util.Comparator[A] of trait Extractor
       trait Extractor[-A,+B] extends Comparator[A]
             ^


scala> trait Extractor[-A, +B] {                 
     |   def comp:Comparator[B]
     | }
<console>:7: error: covariant type B occurs in invariant position in type => java.util.Comparator[B] of method comp
         def comp:Comparator[B]
             ^

Do you see any way out of this or is this just one of those cases where "using java generics in scala hurts"?

Community
  • 1
  • 1
Agl
  • 469
  • 2
  • 13

2 Answers2

12

With help of type-bounds it's possible to do the following:

scala> trait Extractor[-A, +B] {
     | def comp:Comparator[_ <: B]
     | }
defined trait Extractor
Vasil Remeniuk
  • 20,519
  • 6
  • 71
  • 81
  • thanks. That works. The code is not running yet but your code and the effective declaration of the Comparator compile save and sound. Thank you so much. – Agl Sep 15 '10 at 12:53
  • I know this is a whole different question but is there a good paper/tutorial/blog covering type-bounds and variants? I already read about it but one could argue that I didn't learn too much from them ;) (on the other hand, maybe that's just me. So all read them all again) – Agl Sep 15 '10 at 13:01
  • 2
    This one is pretty good (though it's not up-to-date -- doesn't have information about some 2.8 features) : http://programming-scala.labs.oreilly.com/ch12.html – Vasil Remeniuk Sep 15 '10 at 13:06
  • until i DO read more about this, just let me make sure, I understand what you are doing here. `[_ <: B]` says "what ever the type parameter may be, it has to be a subtype of `B`". Right? Shouldn't (in a perfect world) this information derive from the usage of `+B` in `Extractor[-A, +B]`? – Agl Sep 15 '10 at 13:08
  • Thanks for the reading material. Already planed to work my way through that book . . . – Agl Sep 15 '10 at 13:09
7

You can make Extractor[A,B] extend Comparator[A] by using the @uncheckedVariance annotation.

scala> import scala.annotation.unchecked.uncheckedVariance
import scala.annotation.unchecked.uncheckedVariance

scala> trait Extractor[-A,+B] extends java.util.Comparator[A @uncheckedVariance]
defined trait Extractor

@uncheckedVariance is safe here because Comparator could have been defined as Comparator[-T]. There was a discussion around making Ordering covariant for Scala 2.8 using this annotation.

Edit See this question for more about @uncheckedVariance.

Community
  • 1
  • 1
Ben Lings
  • 28,823
  • 13
  • 72
  • 81
  • uh that's a nice one too. Actually this could be the version that ends up in my code . . . as soon as I find the time to read that thread you posted. Thanks a lot! – Agl Sep 15 '10 at 20:17