Reputation:
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
Upvotes: 1
Views: 49
Reputation: 170733
it complains that method run overrides nothing
That's because overriding method must have the same type parameters, you just replace I
and C
, so in this case it must be
new Node[Int,Any] {
override protected def run[O <: Int](item: Int, context: Any, tick: Int): State[O] = ???
override protected def canRun(item: Int, context: Any, tick: Int): Boolean = ???
}
Note that you this signature says "caller can get State[O]
for any O <: Int
it wants", e.g. Nothing
. You can't really meaningfully implement a method with this signature.
If you want to get this signature for run
, then in Node
it probably should be
protected def run(item: I, context: C, tick: Int): State[I]
which means I
can't be contravariant (or covariant, for that matter).
Upvotes: 2
Reputation: 1191
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]
}
Upvotes: 1