You can define a structural type for key , but not for union . A structural type may not be an abstract type defined outside itself. So this will not work:
trait Tree[T <: { def union(x: T): T }]
You can define the trait that Tree elements should do, though:
trait TreeVal[T] { type A def key: A def union(x: T): T }
This can be used in two ways. First, classes must implement this interface, which severely limits which classes can be used as keys. It will look like this:
trait Tree[T <: TreeVal[T]]
It can also be suggested as an implicit conversion, for example:
class IntVal(v: Int) extends TreeVal[Int] { type A = Int def key: A = v def union(x: Int): Int = x + v } implicit def IntIsVal(v: Int): IntVal = new IntVal(v) class Tree[T <% TreeVal[T]]
This used the so-called view binding. Look at that for more information, but suffice it to say that you can handle everything that has an implicit conversion defined in scope, as if it were TreeVal . For instance:
class Tree[T <% TreeVal[T]](node: T, left: Option[Tree[T]], right: Option[Tree[T]]) { override def toString = "(%s < %s > %s)" format (left.getOrElse("o"), node.key, right.getOrElse("o")) }
Alternatively, you can use it with a template of type type with a few changes:
trait TreeVal[T] { type A def key(v: T): A def union(x: T, y: T): T } class Tree[T : TreeVal] // must be class, so it can receive parameters
A type type template uses context boundaries. See that for more information. This style, as a rule, is preferable to the previous style today, because it is more flexible in many respects. However, both will work.
In this case, you can use it as follows:
implicit object IntVal extends TreeVal[Int] { type A = Int def key(v: Int) = v def union(x: Int, y: Int) = x + y } class Tree[T: TreeVal](node: T, left: Option[Tree[T]], right: Option[Tree[T]]) { val treeVal = implicitly[TreeVal[T]] import treeVal._ override def toString = "(%s < %s > %s)" format (left.getOrElse("o"), key(node), right.getOrElse("o")) }