To my best knowledge, a traditional pie template usually includes 1 layer of nesting attributes to group operations together. Then the outer layer declares the actual "service" (here: "Add, Mul, Operations") without defining it.
trait AddComponent[T] { this: FooComponent[T] => def addition: Add trait Add { def constant: T def plus( t1: T, t2: T ): T def add( t: T ) = plus( t, constant ) } } trait MulComponent[T] { this: BarComponent[T] => def multiplication: Mul trait Mul { def constant: T def times( t1: T, t2: T ): T def mul( t: T ) = times( t, constant ) } } trait OperationsComponent[T] { this: Add[T] with Mul[T] => def operations: Operations trait Operations { def neg( t: T ): T } }
Then, when mixing the signs "... Component" are interdependently linked:
trait IntOperations extends Operation[Int] { class IntAdd extends Add { ... } class IntMul extends Mul { ... } } class MyFooBar extends FooComponent[Int] with BarComponent[Int] with IntOperations { lazy val addition = new IntAdd lazy val multiplication = new IntMul lazy val foo = ... lazy val bar = ... }
This solves your specific problem with names, but name conflicts (from the "service" definitions) remain the problem of the traditional cake template. There is a blog post by Daniel Spivak demonstrating how this can be resolved in general, but the solution comes with its own set of (huge) trade-offs (see this talk ).
Hope this helps.
PS instead of type parameters it would be better to use abstract types here.
user500592
source share