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?
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!
There is a familiar example in the spec:
Sequence.append
is example 4.5.2 in the pdf, but the markdown isn't numbered at the moment.
abstract class Sequence[+A] {
def append[B >: A](x: Sequence[B]): Sequence[B]
}
In real life, see the doc for Seq.++
, ignoring the "use case" and clicking on the "full signature" to show the lower bound.
This is the same pattern as in other widening operations like Option.getOrElse
, where you're getting back a possibly wider type than you started with.
Here's an example of how it makes sense in terms of substitution:
Given a Seq[Fruit]
, I can append a Seq[Orange]
. Since Apple <: Fruit
, I can also append the oranges to a Seq[Apple]
and get fruits back.
That's why the B
type parameter wants to be bound by a covariant parameter. The variance position of B
is classified as negative, for purposes of variance checking, but B
itself is not annotated.
Funny thing is that this parses:
scala> trait X { def append[-](): Unit }
defined trait X
来源:https://stackoverflow.com/questions/23369454/why-the-type-position-of-a-method-is-marked-as-negative