Message equivalent of dependent method types

236 Views Asked by At

Thanks to this post I'm getting my head around dependent method types. I have a structure similar to the following

trait Environment{
    type Population <: PopulationBase
    protected trait PopulationBase

    def evolveUs(population: Population): Population
}

object FactoredOut{
    def evolvePopulation(env: Environment)(prevPopulation: env.Population): env.Population = {
        env.evolveUs(prevPopulation)
    }
}

I now want to start using actors to spread the work in the FactoredOut part across a cluster. To do this I need a way to pass immutable messages which carry the Environment.

Obviously the following doesn't work, but demonstrates what I'm trying to do

object Messages{
    case class EvolvePopulation(env: Environment)(prevPopulation: env.Population)
}

What is the correct way to pass the population and it's enclosing environment around?

(Would have added the dependent-method-types tag, but I don't have enough points to add a 'new' tag)

1

There are 1 best solutions below

1
Miles Sabin On BEST ANSWER

Your intuition that you need to pack up both the value of the dependent type (env.Population) and the value that the type depends on (env) as a single object is exactly right.

Given the definitions you've already posted, probably the simplest approach would be something like this,

// Type representing the packaging up of an environment and a population
// from that environment
abstract class EvolvePopulation {
  type E <: Environment
  val env : E
  val prevPopulation : env.Population
}

object EvolvePopulation {
  def apply(env0 : Environment)(prevPopulation0 : env0.Population) =
    new EvolvePopulation {
      type E = env0.type
      val env : E = env0 // type annotation required to prevent widening from
                         // the singleton type
      val prevPopulation = prevPopulation0
    }
}

Now if we define a concrete environment type,

class ConcreteEnvironment extends Environment {
  class Population extends PopulationBase
  def evolveUs(population: Population): Population = population
}

we can use it directly as before,

val e1 = new ConcreteEnvironment

val p1 = new e1.Population
val p2 = e1.evolveUs(p1)
val p3 = e1.evolveUs(p2)

and we can also package up an environment and population for distribution,

def distrib(ep : EvolvePopulation) {
  import ep._
  val p4 = env.evolveUs(prevPopulation)
  val p5 = env.evolveUs(p4)
  val p6 = env.evolveUs(p5)
}

val ep1 = EvolvePopulation(e1)(p3)

distrib(ep1)