Here's what I want to write:
val alg10 = new GeneticAlgorithm[FlatDesign]
with PhenotypeSize(10)
with FlatDesignMutator
with ChildrenReplaceParents(numChildren=2)
with TargetFitnessFunction(targetPhenotype)
with PopulationSize(40)
with RunLength(numGenerations=100)
In other words, there are lots of constraints and parameters I'd like to set. For example, PhenotypeSize(10) has implications for the mutator and the fitness function. Abstract types ought to make it easy to implement/enforce all of those consistency constraints. Of course, traits can't take parameters, so this code won't work.
I love how composing traits lets you mix and match whatever functionality you need. The code above is so readable! With code like that, I could easily write a loop to try out the Cartesian product of lots and lots of variations to the algorithm.
I'm stuck, though, on finding a clean way to supply the parameters for those traits. Any suggestions?
Early definitions are described in Programming in Scala as "pre-initialized fields" or 5.1.6 of the spec and have the semantics you'd expect from trait parameters, namely, they are available in constructors.
val alg10 = new { val size = 10 } with GeneticAlgorithm[FlatDesign]
with PhenotypeSize { val size: Int ; val psize = 2 * size }
This question must be a duplicate.
This may be what you are doing already, but just in case, if you define your traits like this:
trait PhenotypeSize {
val size: Int
...
}
trait FlatDesignMutator { ... }
trait ChildrenReplaceParents {
val numChildren: Int
...
}
...
Then your class will look like:
class GeneticAlgorithm[T]
extends PhenotypeSize
with FlatDesignMutator
with ChildrenReplaceParents
with ... {
override val size: Int = 2
override val numChildren: Int = 10
...
}
The compiler will check that you have given proper definitions for the members size
, numChildren
, and so on in your GeneticAlgorithm
class implementation. This sounds reasonably nice and concise to me.