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

放肆的年华 提交于 2019-11-30 15:00:13
TeWu

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!