scala/ScalaBook/chapter-12/src/main/scala/scalabook/ep/ocgenselfp/base.scala

package scalabook.ep.ocgenselfp

/**
 * Definitions for an Operation-Centric approach with generics
 * with Torgersen's trick to introduce an extra self parameter.
 *
 * These are te base definitions. See sub/sub.scala for data extensions.
 * 
 * @author Christos KK Loverdos
 */

trait BaseD[V <: BaseOp] {
  def perform(op: V)
}

trait BaseOp {
  // Note the extra self parameter that provides the correct V type
  // for data to call perform --> see EvalOp::apply
  def computeNumD[V <: BaseOp](data: NumD[V], self: V)
}

class NumD[V <: BaseOp](val value: Int) extends BaseD[V] {
  def perform(op: V) {
    op.computeNumD(this, op)
  }
}

class EvalOp extends BaseOp {
  var result: Option[Int] = None

  def apply[V <: BaseOp](data: BaseD[V], self: V) = {
    data.perform(self) // self here has the correct value (this doesnot!)
    result.get
  }
  
    def computeNumD[V <: BaseOp](data: NumD[V], self: V) {
      this.result = Some(data.value)
    }
}

class ReprOp extends BaseOp {
    var result: Option[String] = _

    def apply[V <: BaseOp](data: BaseD[V], self: V) = {
        data.perform(self)
        result.get
    }

    def computeNumD[V <: BaseOp](data: NumD[V], self: V) {
        this.result = Some(data.value.toString)
    }
}

object eval {
    def apply[V <: BaseOp](data: BaseD[V], self: V) = new EvalOp()(data, self)
}

object repr {
    def apply[V <: BaseOp](data: BaseD[V], self: V) = new ReprOp()(data, self)
}