Reputation: 31
I'm attempting to make a system which allows users to chain together multiple processors. The difficulty I have is that each processor actually has two bits of information it cares about and I want these handled in a type-safe way. I've boiled it down to this problem:
Given:
//First family of stuff
trait A {
def doA {}
}
trait B {
def doB {}
}
//Second family of stuff
trait X {
def doX {}
}
trait Y {
def doY {}
}
I can combine elements from the two families together in 4 flavors:
var f = new A with X {}
f.doA
d.doX
var g = new A with Y {}
//...
Great. The problem is that I want each of the functions (doA, etc) to return a combination of the two types so that I can chain stuff together. Essentially I want to do: trait A { def doThing = { new A with ThingThatImMixedInWithLikeXOrY {} } }
Each processor needs to return an anonymous class which consists of 1) A type known to the processor 2) The type which it was mixed in with.
My first stab was to use generics, something like this:
trait A {
this : {def makeWithT[TOther]} =>
def doA = makeWithT[B]
}
trait B {
this : {def makeWithT[TOther]} =>
def doB = makeWithT[A]
}
trait X {
this : {def makeWithS[TOther]} =>
def doX = makeWithT[Y]
}
trait Y {
this : {def makeWithS[TOther]} =>
def doY = makeWithT[Y]
}
class Foo[T, S] extends S with T {
def makeWithT[OtherT] = new T with OtherT
def makeWithS[OtherT] = new S with OtherT
}
var f = new Foo[A, X]
f.doA.doB
f.doX.doA
f.doX.doY
...
Obviously, I've run into a catalog of problems:
I can't make a generic class which extends from the type parameters
I can't instantiate my anonymous class via a generic parameter
I can't define the return type of the functions in the trait because I don't know the type until it's mixed in with something.
I'm a bit of a noob when it comes to scala, and I get the feeling that I'm going about this in totally the wrong way and maybe I should be using implicits and the CanBuildFrom pattern. Any help would be greatly appreciated.
Cheers
Upvotes: 3
Views: 590
Reputation: 5977
The most known solution for stackable processors is stackable trait
http://www.artima.com/scalazine/articles/stackable_trait_pattern.html
trait StackableRoot {
def StackablePass (x:Int) : x
}
trait StackableDouble extends StackableRoot {
def internalPass (x:Int) = 2*x
abstract override def StackablePass(x:Int) = super.StackablePass(internalPass(x))
}
trait StackableIncrement extends StackableRoot {
def internalPass (x:Int) = x+1
abstract override def StackablePass(x:Int) = super.StackablePass(internalPass(x))
}
there is some boilerplate with
abstract override def StackablePass(x:Int) = super.StackablePass(internalPass(x))
that can't be avoided by packing into parametrized trait since it will require scala to inherit some trait with different parameters multiple times that is prohibited by famous type erasure
Upvotes: 1