Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
756 views
in Technique[技术] by (71.8m points)

scala - Why method defined like "cons[B >: A](v: B)" accepts argument of type which is not supertype of A?

I am studying variance in scala right now, and I think I have a good understanding of contravariance. For example given trait List[-A], I know that List[Int] is a supertype of List[AnyVal].

But say that I have the following trait:

trait List[+A] {
  def cons(hd: A): List[A]
}

Why is cons parameter type wrong?

Why it is necessary to have def cons[B >: A](v: B): List[B] ?

For example:

val animal_list: List[Animal] = List(tiger, dog)

if we call:

animal_list.cons(tiger)

since Tiger <: Animal, doesn't cons ran into problem? Since B is Tiger and A is Animal and B >: A is not true.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Why is cons's parameter type wrong?

trait List[+A] {
  def cons(hd: A): List[A]
}

Compiler give you error:
covariant type A occurs in contravariant position in type A of value hd
because method parameters count as contravariant positions, but A is covariant.

Let's imagine that this method declaration would compile. Then we could do:

class ListImpl[A] extends List[A] {
  override def cons(hd: A): List[A] = ???
}

val strings: List[String] = new ListImpl[String]
val values: List[Any] = strings // OK, since List[String] <: List[Any] (in List[A], A is covariant)
values.cons(13) // OK(??), since values's static type is List[Any], so argument of cons should be Any, and 13 conforms to type Any

Is the last line above really OK? We are calling cons on values. values is the same as strings, and strings is object of type ListImpl[String]. So cons invocation in the last line is expecting String argument, but we are passing Int, because values's static type is List[Any] and Int conforms to Any. Something is definitely wrong here - which line is to blame? The answer is: cons method declaration. To fix this issue, we have to remove covariant type parameter A from contravariant position (in cons declaration). Alternatively we can make A non-covariant.

See also these questions: #1, #2.

... doesn't cons ran into problem?

trait List[+A] {
  def cons[B >: A](v: B): List[B]
}

val animal_list: List[Animal] = List(tiger, dog)  // We are assuming that List.apply and concrete implementation of List is somewhere defined.

No, animal_list.cons(tiger) invocation is type-correct.

I assume that Animal is common supertype of Dog and Tiger, and that dog and tiger are instances of Dog and Tiger respectively.

In animal_list.cons(tiger) invocation, both A and B type parameters are instantiated to Animal, so cons method takes form of:

def cons[Animal >: Animal](v: Animal): List[Animal]

Animal >: Animal constraint is satisfied because:

Supertype and subtype relationships are reflexive, which means a type is both a supertype and a subtype of itself. [source]

The argument to cons is Tiger which conforms to type Animal, so the method invocation is type-correct.

Notice that if you force B to be instantiated to Tiger, like animal_list.cons[Tiger](tiger), then this invocation won't be type-correct, and you'll get compiler error.

See similar example here.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...