I have a ADT as follows:
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]
When i try to build a function to randomly create Trees i get a problem with the EmptyTree, the type system does not let go through
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)
basically in create(EmptyTree, currentDepth + 1)
it complains that it is expecting a Tree[A]
and is receiving a EmptyTree.type
The compiler objections are justified. The compiler expects
Tree[A]
and you are passingEmptyTree
, whose super type isTree[Nothing]
. A priori, there's no subtyping relationship between these two types.What you want is
Tree
to be covariant: ifX <: Y
thenTree[X] <: Tree[Y]
. Then, asNothing <: A
for anyA
you getEmptyTree.type <: Tree[A]
and you can always passEmptyTree
whenever you need aTree[A]
.The syntax for declaring the
A
parameter inTree
covariant isTree[+A]
; change that and your code should compile.This is a good post on covariance and contravariance in Scala: Be friend with covariance and contravariance
UPDATE After your questioning answer I have actually looked at your constructors for
Tree
and, as defined, you cannot makeTree
covariant. Sadly, the compiler won't complain (you see, it should actually complain more). Yourop
inNode
is contravariant onSeq[A]
, and thus you cannot makeNode
covariant. At this point you might be thinking:Well, through making its supertype
Tree
covariant Node becomes so in practice.scalac
should actually check that all sub type constructors of a covariant one are (or could be made) covariant. Anyway, code showing this follows:UPDATE 2 Back to your original question :) I'd leave your
Tree
type invariant, and have anEmptyTree[A]()
case class; it is a pity that there are no parameterless value classes.