它是类型检查器错误吗?
Is it a type checker bug?
我将其缩小为以下代码:
trait A[T] {
def apply(t: T): Int
}
sealed trait P {
def apply(): Int
}
case class I[T](a: A[T], t: T) extends P {
def apply: Int = a(t)
}
case class X[T1, T2](a1: A[T1], a2: A[T2]) extends A[(T1, T2)] {
def apply(t: (T1, T2)): Int =
t match {
case (t1, t2) => a1(t1) + a2(t2)
}
}
object m {
def apply(p1: P, p2: P): P =
(p1, p2) match {
case (I(a1, t1), I(a2, t2)) =>
I(X(a1, a2), (t2, t1)) // <-- Here
}
}
如您所见,我在标记为 <-- Here
的行中有一个类型错误。然而,代码甚至在没有警告的情况下编译,并在运行时失败并返回 ClassCastException
。要玩的代码:
case class E() extends A[Int] {
def apply(t: Int): Int = t
}
case class S() extends A[String] {
def apply(t: String): Int = t.length
}
object Test {
def apply() = {
val pe: P = I(E(), 3)
val ps: P = I(S(), "abcd")
val pp: P = m(pe, ps)
pp()
}
}
我知道模式匹配 scala 有时无法检查值的类型是否正确,但这通常会导致编译器警告。
所以,这是一个错误,还是我遗漏了什么?
更新:我担心的是我可能会出现类型错误,而编译器甚至不会警告我。我明白 (t1, t2)
是正确的顺序;但是如果我写错了,直到执行程序我才会发现它,甚至可能更晚,尽管它显然是一个类型错误。
简答:键入擦除。
当您在 (p1, p2)
上进行匹配时,您所知道的任何一种类型都是 P
,也可能是 I[T]
,但不一定具有相同的 T
。如果您更明确地使用 I[T]
,您可能会收到类型擦除警告,但由于您已将类型提高到 P
,我的猜测是编译器甚至懒得检查警告。漏洞?可能是。我更愿意称之为缺陷。无论如何,由于 T
的类型信息被删除,编译器将允许这样做。
如果我们将 m.apply
的参数类型更改为 I[T]
,If 会变得更加明显,其中 p1
和 [=22] 的 T
相同=].
object m {
def apply[T](p1: I[T], p2: I[T]): P =
(p1, p2) match {
case (I(a1, t1), I(a2, t2)) =>
I(X(a1, a2), (t2, t1))
}
}
val pe = I(E(), 3)
val ps = I(S(), "abcd")
m(pe, pe).apply // same underlying type, works
m(ps, ps).apply // same underlying type, works
m(pe, ps).apply // doesn't compile
到底是不是你想要的,我不知道。
可能缺少警告与这个有关:
https://issues.scala-lang.org/browse/SI-9188
它似乎对 A 上的类型参数没有做任何有用的事情,除非它可以静态地证明你弄错了。
这里的最后一场比赛应该警告:
scala> val i = I(E(), 42)
i: I[Int] = I(E(),42)
scala> i match { case I(a: A[Int], x) => }
scala> i match { case I(a: A[String], x) => }
<console>:15: warning: non-variable type argument String in type pattern A[String] is unchecked since it is eliminated by erasure
i match { case I(a: A[String], x) => }
^
<console>:15: error: pattern type is incompatible with expected type;
found : A[String]
required: A[Int]
i match { case I(a: A[String], x) => }
^
scala> (i: P) match { case I(a: A[String], x) => }
<console>:15: warning: non-variable type argument String in type pattern A[String] is unchecked since it is eliminated by erasure
(i: P) match { case I(a: A[String], x) => }
^
<console>:15: error: pattern type is incompatible with expected type;
found : A[String]
required: A[Any]
Note: String <: Any, but trait A is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
(i: P) match { case I(a: A[String], x) => }
^
scala> (i: P) match { case I(a: A[Int], x) => }
<console>:15: warning: non-variable type argument Int in type pattern A[Int] is unchecked since it is eliminated by erasure
(i: P) match { case I(a: A[Int], x) => }
^
<console>:15: error: pattern type is incompatible with expected type;
found : A[Int]
required: A[Any]
Note: Int <: Any, but trait A is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
(i: P) match { case I(a: A[Int], x) => }
^
scala> (i: P) match { case I(a: A[_], x) => }
scala> (i: P) match { case I(a: A[Any], x) => }
补充一下:
scala> (i: P) match { case I(a: A[Any], x) => a("foo") }
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)
at E.apply(<console>:33)
... 33 elided
我将其缩小为以下代码:
trait A[T] {
def apply(t: T): Int
}
sealed trait P {
def apply(): Int
}
case class I[T](a: A[T], t: T) extends P {
def apply: Int = a(t)
}
case class X[T1, T2](a1: A[T1], a2: A[T2]) extends A[(T1, T2)] {
def apply(t: (T1, T2)): Int =
t match {
case (t1, t2) => a1(t1) + a2(t2)
}
}
object m {
def apply(p1: P, p2: P): P =
(p1, p2) match {
case (I(a1, t1), I(a2, t2)) =>
I(X(a1, a2), (t2, t1)) // <-- Here
}
}
如您所见,我在标记为 <-- Here
的行中有一个类型错误。然而,代码甚至在没有警告的情况下编译,并在运行时失败并返回 ClassCastException
。要玩的代码:
case class E() extends A[Int] {
def apply(t: Int): Int = t
}
case class S() extends A[String] {
def apply(t: String): Int = t.length
}
object Test {
def apply() = {
val pe: P = I(E(), 3)
val ps: P = I(S(), "abcd")
val pp: P = m(pe, ps)
pp()
}
}
我知道模式匹配 scala 有时无法检查值的类型是否正确,但这通常会导致编译器警告。
所以,这是一个错误,还是我遗漏了什么?
更新:我担心的是我可能会出现类型错误,而编译器甚至不会警告我。我明白 (t1, t2)
是正确的顺序;但是如果我写错了,直到执行程序我才会发现它,甚至可能更晚,尽管它显然是一个类型错误。
简答:键入擦除。
当您在 (p1, p2)
上进行匹配时,您所知道的任何一种类型都是 P
,也可能是 I[T]
,但不一定具有相同的 T
。如果您更明确地使用 I[T]
,您可能会收到类型擦除警告,但由于您已将类型提高到 P
,我的猜测是编译器甚至懒得检查警告。漏洞?可能是。我更愿意称之为缺陷。无论如何,由于 T
的类型信息被删除,编译器将允许这样做。
m.apply
的参数类型更改为 I[T]
,If 会变得更加明显,其中 p1
和 [=22] 的 T
相同=].
object m {
def apply[T](p1: I[T], p2: I[T]): P =
(p1, p2) match {
case (I(a1, t1), I(a2, t2)) =>
I(X(a1, a2), (t2, t1))
}
}
val pe = I(E(), 3)
val ps = I(S(), "abcd")
m(pe, pe).apply // same underlying type, works
m(ps, ps).apply // same underlying type, works
m(pe, ps).apply // doesn't compile
到底是不是你想要的,我不知道。
可能缺少警告与这个有关:
https://issues.scala-lang.org/browse/SI-9188
它似乎对 A 上的类型参数没有做任何有用的事情,除非它可以静态地证明你弄错了。
这里的最后一场比赛应该警告:
scala> val i = I(E(), 42)
i: I[Int] = I(E(),42)
scala> i match { case I(a: A[Int], x) => }
scala> i match { case I(a: A[String], x) => }
<console>:15: warning: non-variable type argument String in type pattern A[String] is unchecked since it is eliminated by erasure
i match { case I(a: A[String], x) => }
^
<console>:15: error: pattern type is incompatible with expected type;
found : A[String]
required: A[Int]
i match { case I(a: A[String], x) => }
^
scala> (i: P) match { case I(a: A[String], x) => }
<console>:15: warning: non-variable type argument String in type pattern A[String] is unchecked since it is eliminated by erasure
(i: P) match { case I(a: A[String], x) => }
^
<console>:15: error: pattern type is incompatible with expected type;
found : A[String]
required: A[Any]
Note: String <: Any, but trait A is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
(i: P) match { case I(a: A[String], x) => }
^
scala> (i: P) match { case I(a: A[Int], x) => }
<console>:15: warning: non-variable type argument Int in type pattern A[Int] is unchecked since it is eliminated by erasure
(i: P) match { case I(a: A[Int], x) => }
^
<console>:15: error: pattern type is incompatible with expected type;
found : A[Int]
required: A[Any]
Note: Int <: Any, but trait A is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
(i: P) match { case I(a: A[Int], x) => }
^
scala> (i: P) match { case I(a: A[_], x) => }
scala> (i: P) match { case I(a: A[Any], x) => }
补充一下:
scala> (i: P) match { case I(a: A[Any], x) => a("foo") }
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)
at E.apply(<console>:33)
... 33 elided