How to pass extential type of tuple to invariant generic function?

青春壹個敷衍的年華 提交于 2020-05-17 06:13:07

问题


I have an extential type of tuple, and I want to pass it to a generic function (I use ClassTag in this example but it is a custom type class with invariant type parameter):

type TC = (ClassTag[T], Option[T]) forSome {type T}
def foo[T](ct: ClassTag[T], o: Option[T]) = {}

val tc: TC = (classTag[String], Option[String](null))
foo(tc._1, tc._2)

This gives me error:

error: type mismatch; found : scala.reflect.ClassTag[T] required: scala.reflect.ClassTag[Any] Note: T <: Any, but trait ClassTag is invariant in type T. You may wish to investigate a wildcard type such as _ <: Any. (SLS 3.2.10) foo(tc._1, tc._2)

I wanna ensure the type of two parameters ct and o uses the same parameter type T and I thought extential type should ensure this, but it does not seem to work.

However if I don't use tuple and only use ClassTag, it works fine:

type TC = ClassTag[T] forSome {type T}
def foo[T](ct: ClassTag[T]) = {}

val tc: TC = classTag[String]
foo(tc)

So the previous error of ClassTag invariance does not make sense.

Why does it not work when I use a tuple? How can I make it work?


回答1:


For val tc: TC = classTag[String], Option[String](null)) tc._1 has type ClassTag[_] (which is different from any ClassTag[T] e.g. ClassTag[Any] because ClassTag is invariant), tc._2 has type Option[_], which is the same as Option[Any] because Option is covariant

implicitly[Option[_] =:= Option[Any]]
implicitly[Option[Any] =:= Option[_]]

An existential type 𝑇 forSome { 𝑄 } where 𝑄 contains a clause type 𝑡[tps]>:𝐿<:𝑈 is equivalent to the type 𝑇′ forSome { 𝑄 } where 𝑇′ results from 𝑇 by replacing every covariant occurrence of 𝑡 in 𝑇 by 𝑈 and by replacing every contravariant occurrence of 𝑡 in 𝑇 by 𝐿.

https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#existential-types

So in

def foo[T](ct: ClassTag[T], o: Option[T]) = {}
foo(tc._1, tc._2)

we have the type mismatch. Any and _ (aka arbitrary T) are different types.

Try to use universal quantification rather than existential

type TC[T] = (ClassTag[T], Option[T])
def foo[T](ct: ClassTag[T], o: Option[T]) = {}

val tc: TC[String] = (classTag[String], Option[String](null))
foo(tc._1, tc._2)


来源:https://stackoverflow.com/questions/57731598/how-to-pass-extential-type-of-tuple-to-invariant-generic-function

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