Вопрос

I have a trait called Mutatable that spits out a modified copy of an implementing class. I also have a trait I'd like to stack on top of it called CostedMutatable that keeps track of the cost of doing so. The method applyMutation returns an Option, as later I'd like to return None in cases where a particular mutation doesn't apply.

A simple version that just works on Ints (and "mutates" them by adding in new numbers) is shown below:

trait Mutatable[M] {

  def applyMutation(mut : M) : Option[this.type]
}

trait CostedMutatable[M] extends Mutatable[M]{

  var cost : Int = _
  def getCostFor(mut : M): Int

  abstract override def applyMutation(mut : M) : Option[this.type] = {
    cost += getCostFor(mut)
    applyMutation(mut)
  }
}

object Example extends App {

  case class Mutation(x: Int)

  class Test(s: Int) extends Mutatable[Mutation] {
    val start = s
    override def applyMutation(mut: Mutation): Option[Test] 
         = Some(new Test(s+mut.x))
  }

  class CostTest(s: Int) extends Test(s) with CostedMutatable[Mutation] {
    override def getCostFor(mut: Mutation): Int = 2
  }

  val testCost = new CostTest(5).cost
}

The problem is, this won't compile. I get the following error on compilation:

Error:(23, 18) overriding method applyMutation in trait Mutatable of type (mut: Example.Mutation)Option[Test.this.type];
 method applyMutation has incompatible type
    override def applyMutation(mut: Mutation): Option[Test] = Some(new Test(s+mut.x))
                 ^

Aside from the compiler error, one other question comes to mind: am I even approaching this the right way? Should I be using F-bounded types instead? (I'll need each new implementing class to return a new copy of the concrete implementing class from applyMutation.)

Thanks in advance.

Это было полезно?

Решение

this.type is a type the only instances of which are this and Nothing. When a method returns this.type, the only allowed return value is this. In class Test applyMutation doesn't return this, but rather a completely new Test, which isn't an instance of this.type. This is why the code does not type-check.

I think what you are really trying to do is declare that applyMutation returns a value of the same class as this. Doing this does indeed require F-Bounded polymorphism. Here is a rewritten version of your code:

trait CostedMutatable[+A <: CostedMutatable[A, M], M] extends Mutatable[A, M] {

  var cost : Int = _
  def getCostFor(mut : M): Int

  abstract override def applyMutation(mut: M): Option[A] = {
    cost += getCostFor(mut)
    super.applyMutation(mut)
  }
}

object Example extends App {

  case class Mutation(x: Int)

  class Test(s: Int) extends Mutatable[Test, Mutation] {
    val start = s
    override def applyMutation(mut: Mutation): Option[Test] 
         = Some(new Test(s+mut.x))
  }

  class CostTest(s: Int) extends Test(s) with CostedMutatable[CostTest, Mutation] {
    override def getCostFor(mut: Mutation): Int = 2
  }

  val testCost = new CostTest(5).cost
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top