方法的右手边(模式匹配)
t match {
case Empty => Empty
case NonEmpty(elem, left, right) =>
if (elem < 0) throw new Exception
else NonEmpty(elem, assertNonNegatve(left), assertNonNegative(right))
}
表示在运行时检查t是否是类Empty$(对象Empty)的实例,然后选择第一个分支,否则t是否是类NonEmpty的实例,然后选择第二个分支。
签名
def assertNonNegative[S <: IntTree](t: S): S
表示在编译时检查每个类型S,这是IntTree类型的子类型,如果该方法接受S类型的参数t,则该方法返回一个@987654335类型的值@。
代码无法编译,因为方法的定义与其签名不对应。 IntTree 的子类是 NonEmpty 和 Empty(对象)。如果IntTree 未密封,您可以创建不同于Empty 和NonEmpty 的子类,您甚至可以在运行时动态创建它们。但我们甚至假设IntTree 是密封的,Empty 和NonEmpty 是它的唯一子类。
问题是IntTree有很多子类型(类和类型是different):IntTree、Empty.type、NonEmpty、Nothing、Null、Empty.type with NonEmpty , NonEmpty with SomeType, Empty.type with SomeType, IntTree with SomeType, T (type T <: IntTree), x.type (val x: IntTree = ???) 等等,所有这些条件都必须满足(t: S): S。
显然这不是真的。例如我们可以取t = Empty.asInstanceOf[Empty.type with Serializable]。它的类型为Empty.type with Serializable。在运行时它匹配类Empty(对象),因此选择了第一个分支。但是在编译时我们还不知道这一点,如何在编译时保证返回的Empty 和NonEmpty 都是Empty.type with Serializable 类型?
Type mismatch on abstract type used in pattern matching
修复assertNonNegative 的一种方法是编写诚实的单态
def assertNonNegative(t: IntTree): IntTree = {
t match {
case Empty => Empty
case NonEmpty(elem, left, right) =>
if (elem < 0) throw new Exception
else NonEmpty(elem, assertNonNegative(left), assertNonNegative(right))
}
}
另一种是假装多态签名是正确的
def assertNonNegative[S <: IntTree](t: S): S = {
(t match {
case Empty => Empty
case NonEmpty(elem, left, right) =>
if (elem < 0) throw new Exception
else NonEmpty(elem, assertNonNegative(left), assertNonNegative(right))
}).asInstanceOf[S]
}
三是使用类型标签
def assertNonNegative[S <: IntTree : TypeTag](t: S): S = {
t match {
case Empty if typeOf[S] == typeOf[Empty.type] => Empty.asInstanceOf[S]
case NonEmpty(elem, left, right) if typeOf[S] == typeOf[NonEmpty] =>
if (elem < 0) throw new Exception
else NonEmpty(elem, assertNonNegative(left), assertNonNegative(right)).asInstanceOf[S]
case _ => ???
}
}
第四是让ADT更加类型化
sealed trait IntTree
object Empty extends IntTree
case class NonEmpty[L <: IntTree, R <: IntTree](elem: Int, left: L, right: R) extends IntTree
并定义类型类
def assertNonNegative[S <: IntTree](t: S)(implicit ann: AssertNonNegative[S]): S = ann(t)
trait AssertNonNegative[S <: IntTree] {
def apply(t: S): S
}
object AssertNonNegative {
implicit val empty: AssertNonNegative[Empty.type] = { case Empty => Empty }
implicit def nonEmpty[L <: IntTree : AssertNonNegative,
R <: IntTree : AssertNonNegative]: AssertNonNegative[NonEmpty[L, R]] = {
case NonEmpty(elem, left, right) =>
if (elem < 0) throw new Exception
else NonEmpty(elem, assertNonNegative(left), assertNonNegative(right))
}
}
Soundness 的系统类型意味着有时我们在编译时拒绝某些程序,而它们在运行时不会出错。例如
val x: Int = if (true) 1 else "a"