I am attempting to build a behaviour tree library in scala but I am having trouble with the variance. The following is a simplified version of a Node:
sealed trait State[+O]
case class Running[+O](curOut: O, node: Node[O,_]) extends State[O]
case class Success[+O](out: O) extends State[O]
case object Failure extends State[Nothing]
trait Node[-I,C] {
protected def canRun(item: I, context: C, tick: Int): Boolean
protected def run[O <: I](item: I, context: C, tick: Int): State[O]
def apply[O <: I](item: I, context: C, tick: Int): State[O] = {
if (canRun(item, context, tick)) run(item, context, tick)
else Failure
}
}
whenever I try to instantiate Node
however, it complains that method run overrides nothing
:
val node = new Node[Int,Any] {
override protected def run(item: Int, context: Any, tick: Int): State[Int] = ???
override protected def canRun(item: Int, context: Any, tick: Int): Boolean = ???
}
I have tried changing O
in Node
to a type member but that complains that Covariant I is used in contravariant position
:
type O <: I
What I am trying to ask is how can I use a contravariant type parameter as the type for a method parameter and for a method return type? Ideally I would not like to have to make I
contravariant for reusability purposes in parent and decorator nodes.
Thanks in advance
To answer your question
how can I use a contravariant type parameter as the type for a method parameter and for a method return type?
You can't, a conta- (or co)-variant type parameter cannot be used in both a method parameter and the return type.
My recommendation to work around this is to either remove the node
parameter from the Running
case class. Or to replace the parameter with a read-only trait. For example:
case class Running[+O](curOut: O, process: Process[O]) extends State[O]
trait Process[+O] {
def currentState: State[O]
}