我的ADT如下:
sealed trait Tree[A]
case object EmptyTree extends Tree[Nothing]
case class Leaf[A](value: A) extends Tree[A]
case class Node[A](op: Seq[A] => A, branches: Tree[A]*) extends Tree[A]
当我尝试构建一个随机创建树的函数时,我遇到了EmptyTree的问题,类型系统没有让它通过
def create(acc: Tree[A], currentDepth: Int): Tree[A] = currentDepth match {
case maxDepth => Leaf(terminalSet(r.nextInt(terminalSet.length)))
case 0 => {
val op_pos = r.nextInt(fSetLength)
val branches: Seq[Tree[A]] = for (i <- 0 to r.nextInt(fSetLength)) yield create(EmptyTree, currentDepth+1)
Node(functionSet(op_pos)._1, branches:_*)
}
case _ => {
if (r.nextFloat() <= probF) {
val op_pos = r.nextInt(fSetLength)
val branches = for (i <- 0 to r.nextInt(fSetLength)) yield create(EmptyTree, currentDepth + 1)
Node(functionSet(op_pos)._1, branches:_*)
}
else
Leaf(terminalSet(r.nextInt(terminalSet.length)))
}
}
create(EmptyTree, 0)
基本上在create(EmptyTree, currentDepth + 1)
,它抱怨它期待Tree[A]
并且正在接收EmptyTree.type
答案 0 :(得分:7)
编译器异议是合理的。编译器期望Tree[A]
并且您正在传递EmptyTree
,其超类型为Tree[Nothing]
。先验地,这两种类型之间没有子类型关系。
你想要的是Tree
协变:if X <: Y
然后是Tree[X] <: Tree[Y]
。然后,作为任何 Nothing <: A
的{{1}},您获得A
,并且只要您需要EmptyTree.type <: Tree[A]
,就可以随时通过EmptyTree
。< / p>
用于在Tree[A]
协变中声明A
参数的语法是Tree
;改变它,你的代码应该编译。
这是关于Scala中协方差和逆变的好帖子:Be friend with covariance and contravariance
更新在我questioning answer https://www.digitalocean.com/community/tutorials/how-to-use-pageant-to-streamline-ssh-key-authentication-with-putty之后,我实际查看了Tree[+A]
的构造函数,并且根据定义,您无法生成{{1}协变。遗憾的是,编译器不会抱怨(你看,它实际上应该抱怨更多)。 Tree
Tree
中的op
在Node
上是逆变的,因此您无法使Seq[A]
协变。此时你可能会想:
谁在乎
Node
?我只想让Node
变得协变!
好,通过使其超类型Tree
协变节点在实践中变得如此。 Tree
实际应该检查协变者的所有子类型构造函数是否(或可能是)协变的。无论如何,代码显示如下:
scalac
更新2 回到原来的问题:)我将保留// you need a value for EmptyTree! thus default
def evaluateTree[Z](tree: Tree[Z], default: Z): Z =
tree match {
case EmptyTree => default
case Leaf(value) => value
// note how you need to essentially cast here
case Node(op: (Seq[Z] => Z), args @ _*) =>
op(args map { branches => evaluateTree(branches, default) })
}
trait A
trait B extends A
val notNice: Tree[A] = Node[B]({ bs: Seq[B] => bs.head }, EmptyTree)
// ClassCastException!
val uhoh = evaluateTree(notNice, new A {})
类型不变量,并拥有Tree
案例类;遗憾的是没有无参数值类。
EmptyTree[A]()