5

In Scala 2.10.4 this compiles:

trait Foo[-U,T]{
  type Contra = U
}

but in 2.11.0 the same fails with:

contravariant type U occurs in invariant position in type U of type Contra trait Foo[-U,T] {type Contra = U}

Is there a workaround available? Trying to port over a Scala library to 2.11 and the contravariant type is required in order to get a boatload of implicit defs picked up by the compiler (i.e. making U invariant doesn't seem to be an option).

Thanks

virtualeyes
  • 11,147
  • 6
  • 56
  • 91
  • 1
    Could you explain more about how you use the type member and contravariance to control implicit resolution? I'd be interested in hearing more about this, and maybe there is a workaround somewhere deeper in the problem. – wingedsubmariner Apr 30 '14 at 14:59
  • Can you use `type Contra >: U`? – n. m. could be an AI May 01 '14 at 04:31
  • @n.m. I cannot use Contra >: U as the instances that depend on U expect a U and not a supertype of U (i.e. when trying this approach compiler blows up saying that method X is not a member of Contra) – virtualeyes May 01 '14 at 06:30
  • @wingedsubmariner U is required to be a contravariant type argument to Foo, but then U is a covariant type in implicit evidence used throughout the library. Basically Contra must equal exactly U within Foo, but be seen as contra/covariant everywhere else. The quick & dirty for 2.11 is `@uncheckedVariance` – virtualeyes May 01 '14 at 06:41
  • But you can pass some supertype of U to classes that extend Foo , which is exactly the same situation. – n. m. could be an AI May 01 '14 at 06:55
  • @n.m. I shouldn't have said instances, as that implies classes extending Foo which is not the case. The library extends TupleN with a boatload (1-22) of implicit defs that `new Foo[(U1,U2),(T1,T2)] {override def x(c: Contra) = ...}` the Foo trait. When you make Contra a supertype of U in Foo, the overriden method x's Contra does not know of the methods/properties specific to U. – virtualeyes May 01 '14 at 07:23

2 Answers2

4

I can't imagine there being a work around available. The reason I say this is all about supporting path-dependent types:

 def foo[T <: Foo[A,B]](that: T): that.Contra

which places the Contra type in the wrong position. You can not return a contravariant type as a result of an operation. Perhaps the search and validation of these requires so much work that the compiler authors decided this small corner case created too much of a burden or it is a compiler bug that you've uncovered.

By the way, this is just wild speculation on my part. I'd have to read the compiler code to figure out which way is which.

wingedsubmariner
  • 13,350
  • 1
  • 27
  • 52
wheaties
  • 35,646
  • 15
  • 94
  • 131
4

Apparently post-2.7 and pre-2.11 this was a bug in the type checker. Now the prevention of approach in OP is a feature, which is a good thing, kind of o_O

The workaround is to do as one did pre-2.11, the difference being that now you know you're on your own, vs. before where you thought the compiler had your back.

Ignorance is bliss, right?

The workaround:

import annotation.unchecked._
trait Foo[-U,T]{
  type Contra = (U @uncheckedVariance)
}
virtualeyes
  • 11,147
  • 6
  • 56
  • 91