scala正确定义抽象数据类型的空值

时间:2016-12-16 11:59:27

标签: scala generics abstract-syntax-tree covariance abstract-data-type

我的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

1 个答案:

答案 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中的opNode上是逆变的,因此您无法使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]()