I don't seem to understand the Scala type system. I'm trying to implement two base traits and a trait for a family of algorithms to work with them. What am I doing wrong in the below?
The base traits for moves & states; these are simplified to just include methods that expose the problem.
trait Move
trait State[M <: Move] {
def moves: List[M]
def successor(m: M): State[M]
}
Here's the trait for the family of algorithms that makes use of above. I'm Not sure this is right! There might be some +M / -S stuff involved...
trait Algorithm {
def bestMove[M <: Move, S <: State[M]](s: S): M
}
Concrete move and state:
case class MyMove(x: Int) extends Move
class MyState(val s: Map[MyMove,Int]) extends State[MyMove] {
def moves = MyMove(1) :: MyMove(2) :: Nil
def successor(p: MyMove) = new MyState(s.updated(p, 1))
}
I'm on very shaky ground regarding the below, but the compiler seems to accept it... Attempting to make a concrete implementation of the Algorithm trait.
object MyAlgorithm extends Algorithm {
def bestMove(s: State[Move]) = s.moves.head
}
So far there are no compile errors; they show up when I try to put all the parts together, however:
object Main extends App {
val s = new MyState(Map())
val m = MyAlgorithm.bestMove(s)
println(m)
}
The above throws this error:
error: overloaded method value bestMove with alternatives:
(s: State[Move])Move <and>
[M <: Move, S <: State[M]](s: S)M
cannot be applied to (MyState)
val m = MyAlgorithm.bestMove(s)
^
Update: I changed the Algorithm trait to use abstract type members, as
suggested. This solved the question as I had phrased it but I had
simplified it a bit too much. The MyAlgorithm.bestMove()
method must be
allowed to call itself with the output from s.successor(m), like this:
trait Algorithm {
type M <: Move
type S <: State[M]
def bestMove(s: S): M
}
trait MyAlgorithm extends Algorithm {
def score(s: S): Int = s.moves.size
def bestMove(s: S): M = {
val groups = s.moves.groupBy(m => score(s.successor(m)))
val max = groups.keys.max
groups(max).head
}
}
The above gives now 2 errors:
Foo.scala:38: error: type mismatch;
found : State[MyAlgorithm.this.M]
required: MyAlgorithm.this.S
val groups = s.moves.groupBy(m => score(s.successor(m)))
^
Foo.scala:39: error: diverging implicit expansion for type Ordering[B]
starting with method Tuple9 in object Ordering
val max = groups.keys.max
^
Do I have to move to an approach using traits of traits, aka the Cake pattern, to make this work? (I'm just guessing here; I'm thoroughly confused still.)
M
andS
inMyAlgorithm
? – kiritsukuMyAlgorithm
to be generic and work for many concrete implementations of M and S. – Stig Brautaset