为什么多态函数不能接受 Scala 中的通配符(存在)类型?
Why can't a polymorphic function accept wildcard (existential) types in Scala?
在下面的示例中,我想知道为什么 funPoly
不能接受存在量化类型值 outersFromInnersEx
,尽管 funEx
可以。
case class InnerCassClass[I, E, O](i: I, e: E, o: O)
case class OuterCaseClass[I, E, O](inner: InnerCassClass[I, E, O])
val inner1 = InnerCassClass(5, "foo", 3.3f)
val inner2 = InnerCassClass(4.4f, 6, "bar")
// Doesn't work as expected due to invariance of type paramemters I, E, O (but confirm)
// val outersFromInnersAny: List[OuterCaseClass[Any, Any, Any]] = List(inner1, inner2).map(OuterCaseClass.apply)
val outersFromInnersEx: List[OuterCaseClass[_, _, _]] = List(inner1, inner2).map(OuterCaseClass.apply)
def funPoly[I, E, O](occ: List[OuterCaseClass[I, E, O]]): Unit = ()
def funEx(occ: List[OuterCaseClass[_, _, _]]): Unit = ()
// This doesn't work, but why?
val u1 = funPoly(outersFromInnersEx)
val u2 = funEx(outersFromInnersEx)
请注意,我在 Scala 3 (try online) 中对此进行了测试,但在很大程度上问题在 Scala 2 中是相同的,尽管这个特定示例在 Scala 2 中还有其他问题。
关于type variances,您可以通过更改
使funPoly
工作
case class InnerCassClass[I, E, O](i: I, e: E, o: O)
case class OuterCaseClass[I, E, O](inner: InnerCassClass[I, E, O])
到
case class InnerCassClass[+I, +E, +O](i: I, e: E, o: O)
case class OuterCaseClass[+I, +E, +O](inner: InnerCassClass[I, E, O])
请注意,这两个是非常不同的类型:
def funPoly[I, E, O](occ: List[OuterCaseClass[I, E, O]]): Unit = ()
def funEx(occ: List[OuterCaseClass[_, _, _]]): Unit = ()
第一个能够处理 OuterCaseClass
个对象的统一列表,其中每个对象的类型参数都相同。第二个可以处理 OuterCaseClass
对象的混合列表,其中每个对象的类型参数(可能)不同。
使类型参数协变“修复”了问题,因为 List[OuterCaseClass[_, _, _]]
等同于 List[OuterCaseClass[Any, Any, Any]]
,您可以简单地将 funPoly
的类型参数实例化为 Any, Any, Any
让它接受 funEx
可以接受的任何东西。
一般来说,您可以将存在限定类型传递给多态函数。例如,这应该有效:
case class ListAndFunction[A](list: List[A], function: A => Int)
val a: ListAndFunction [_] = ListAndFunction[String](List("a"), _.length)
def mapFunction[A](a: ListAndFunction [A]): List[Int] =
a.list.map(a.function)
mapFunction(a)
在下面的示例中,我想知道为什么 funPoly
不能接受存在量化类型值 outersFromInnersEx
,尽管 funEx
可以。
case class InnerCassClass[I, E, O](i: I, e: E, o: O)
case class OuterCaseClass[I, E, O](inner: InnerCassClass[I, E, O])
val inner1 = InnerCassClass(5, "foo", 3.3f)
val inner2 = InnerCassClass(4.4f, 6, "bar")
// Doesn't work as expected due to invariance of type paramemters I, E, O (but confirm)
// val outersFromInnersAny: List[OuterCaseClass[Any, Any, Any]] = List(inner1, inner2).map(OuterCaseClass.apply)
val outersFromInnersEx: List[OuterCaseClass[_, _, _]] = List(inner1, inner2).map(OuterCaseClass.apply)
def funPoly[I, E, O](occ: List[OuterCaseClass[I, E, O]]): Unit = ()
def funEx(occ: List[OuterCaseClass[_, _, _]]): Unit = ()
// This doesn't work, but why?
val u1 = funPoly(outersFromInnersEx)
val u2 = funEx(outersFromInnersEx)
请注意,我在 Scala 3 (try online) 中对此进行了测试,但在很大程度上问题在 Scala 2 中是相同的,尽管这个特定示例在 Scala 2 中还有其他问题。
关于type variances,您可以通过更改
使funPoly
工作
case class InnerCassClass[I, E, O](i: I, e: E, o: O)
case class OuterCaseClass[I, E, O](inner: InnerCassClass[I, E, O])
到
case class InnerCassClass[+I, +E, +O](i: I, e: E, o: O)
case class OuterCaseClass[+I, +E, +O](inner: InnerCassClass[I, E, O])
请注意,这两个是非常不同的类型:
def funPoly[I, E, O](occ: List[OuterCaseClass[I, E, O]]): Unit = ()
def funEx(occ: List[OuterCaseClass[_, _, _]]): Unit = ()
第一个能够处理 OuterCaseClass
个对象的统一列表,其中每个对象的类型参数都相同。第二个可以处理 OuterCaseClass
对象的混合列表,其中每个对象的类型参数(可能)不同。
使类型参数协变“修复”了问题,因为 List[OuterCaseClass[_, _, _]]
等同于 List[OuterCaseClass[Any, Any, Any]]
,您可以简单地将 funPoly
的类型参数实例化为 Any, Any, Any
让它接受 funEx
可以接受的任何东西。
一般来说,您可以将存在限定类型传递给多态函数。例如,这应该有效:
case class ListAndFunction[A](list: List[A], function: A => Int)
val a: ListAndFunction [_] = ListAndFunction[String](List("a"), _.length)
def mapFunction[A](a: ListAndFunction [A]): List[Int] =
a.list.map(a.function)
mapFunction(a)