1

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?

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

In any case you can't mix the same trait with different constructor parameters. And it is a serious limitation of Cake Pattern and similar techniques.
Thanks for the pointers to the specs! I didn't realize how readily available they were. I've been trial-and-erroring my way to the solution you posted (putting the early initializer first).
Is there a way to make it easy to take the Cartesian product of many "traits"? "Ideal code" would look something like: for (competition <- Seq(ChildrenReplaceParents, ParentsCompeteWithChildren; numChildren <- Seq(2, 3); fitnessFunc <- Seq(TargetFitnessFunc(target), SimpleFitnessFunc, RandomFitnessFunc); ... Using traits is not a requirement; perhaps just passing a bunch of objects fits the problem better. Does Scala reflection provide a way to make this easy—or perhaps some feature I haven't even considered?
1

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.

1 Comment

What you've written indeed looks a lot like what I've been hacking out this morning. :) It's not as pristinely readable as the ideal code above, but it's definitely workable. Please see my follow-up question to @som-snytt about walking through the Cartesian product of traits.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.