abstract class IntTree
object Empty extends IntTree
case class NonEmpty(elem: Int, left: IntTree, right: IntTree) extends IntTree
def assertNonNegative[S <: IntTree](t: S): S = {
t match {
case Empty => Empty // type mismatch, required: S, found: Empty.type
case NonEmpty(elem, left, right) =>
if (elem < 0) throw new Exception
else NonEmpty(elem, assertNonNegatve(left), assertNonNegative(right)) // req: S, fd: NonEmpty.type
}
}
这是我用签名def assertNonNegative[S <: IntTree](t: S): S
实现该功能的失败尝试。除了将签名更改为def assertNonNegative(t: IntTree): IntTree
之外,我找不到实现它的方法。
示例的相关性:
在“ Scala中的函数编程原理”课程中有关子类型和泛型(4.4)的视频中,马丁·奥德斯基(Martin Odersky)实际上使用了相同的示例(IntSet代替IntTree),并说该签名可用于表示该函数将Empty用作空和不空到不空。他说,另一种签名在大多数情况下都很好,但是如果需要,具有上限S的签名可能是一个更精确的选择。但是,他没有显示该功能的实现。
我在这里想念什么?
答案 0 :(得分:6)
方法的右侧(模式匹配)
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
的子类型t
,如果该方法接受类型S
的参数S
,则方法方法返回类型为IntTree
的值。
由于方法的定义与其签名不符,因此无法编译代码。 NonEmpty
的子类是Empty
和IntTree
(对象)。如果未密封Empty
,则可以创建不同于NonEmpty
和IntTree
的子类,甚至可以在运行时动态创建它们。但是,我们甚至假设Empty
是密封的,而NonEmpty
和IntTree
是其唯一的子类。
问题是IntTree
有很多子类型(类和类型是different):Empty.type
,NonEmpty
,Nothing
,{ {1}},Null
,Empty.type with NonEmpty
,NonEmpty with SomeType
,Empty.type with SomeType
,IntTree with SomeType
,T
(type T <: IntTree
),{{ 1}}(x.type
)等条件,并且必须满足所有条件val x: IntTree = ???
。
显然这不是事实。例如,我们可以采用(t: S): S
。它的类型为t = Empty.asInstanceOf[Empty.type with Serializable]
。在运行时,它与类Empty.type with Serializable
(对象)匹配,因此选择了第一个分支。但是在编译时我们还不知道这一点,如何保证在编译时返回的Empty
和Empty
都具有类型NonEmpty
?
Type mismatch on abstract type used in pattern matching
一种修复Empty.type with Serializable
的方法是怀有诚实的单态性
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]
}
第四点是使ADT更具类型级别
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 _ => ???
}
}
并定义类型类
sealed trait IntTree
object Empty extends IntTree
case class NonEmpty[L <: IntTree, R <: IntTree](elem: Int, left: L, right: R) extends IntTree
Soundness类型的系统意味着有时我们在编译时拒绝某些程序,而它们在运行时不会出错。例如
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))
}
}