Search code examples
scalacontravariancetype-systems

Why the type position of a method is marked as negative?


Sorry I have asked some questions like this one, but I still can't get a clear answer, maybe my bad English and unclear expression puzzled the kind people.

When I read the "Type Parameterization" in this article: http://www.artima.com/pins1ed/type-parameterization.html, I see there are some explanation about the type positions:

As a somewhat contrived example, consider the following class definition, where the variance of several positions is annotated with ^+ (for positive) or ^- (for negative):

   abstract class Cat[-T, +U] {
     def meow[W^-](volume: T^-, listener: Cat[U^+, T^-]^-)
     : Cat[Cat[U^+, T^-]^-, U^+]^+
   }

I can understand most of this class, except the W position. I don't understand why it marked as negative, and there is no explanation in the whole document.

It also says:

Type parameters annotated with + may only be used in positive positions, while type parameters annotated with - may only be used in negative positions.

How can I find a type with - annotation in position W to fit this negative position?


Solution

  • The language reference says:

    • The variance position of a method parameter is the opposite of the variance position of the enclosing parameter clause.
    • The variance position of a type parameter is the opposite of the variance position of the enclosing type parameter clause.
    • The variance position of the lower bound of a type declaration or type parameter is the opposite of the variance position of the type declaration or parameter.

    OK what does it mean for a type parameter to have a variance position?

    class Moo[+A, -B] {
      def foo[X] (bar : Y) ...
    

    So Y is in a contravariant position, this is clear. We can put B in its position, but not A.

    But what does it mean for X to be in a contravariant position? We cannot substitute A or B or anything there, it's just a formal parameter!

    That's true, but this thing can have subordinate positions which are types, and have variance. So we need to count the position of X when tracking how many times we flip variance. There's no subordinate clauses of X here, but consider this:

    class Moo[+A, -B] {
      def foo[X >: Z] (bar : B) ...
    

    We probably can replace Z with either A or B, but which is correct? Well, the position of Z is the opposite of that of X, and the position of X is the opposite of that of the top-level, which is covariant, so Z must be covariant too. Let's check:

    abstract class Moo[+A, -B] {
          def foo[X >: A] (bar : B)
    }    
    defined class Moo
    

    Looks like we are right!