Reputation: 1940
Let's build up some context. Data
composes of Metadata
and Payload
.
trait Data[Metadata <: Product, Payload <: Product] extends Product {
def metadata: Metadata
def payload: Payload
}
case class M()
case class P()
case class D(metadata: M, payload: P) extends Data[M, P]
I have Processor
to process Metadata
and Payload
differently.
trait MetadataProcessor[Metadata <: Product] {
def apply() = "process metadata"
}
trait PayloadProcessor[Payload <: Product] {
def apply() = "process payload"
}
// both work
new MetadataProcessor[M]{}.apply() // "process metadata"
new PayloadProcessor[P]{}.apply() // "process payload"
I hope DataProcessor
to be able to process both Metadata
and Payload
. This is one way:
trait DataProcessor[Metadata <: Product, Payload <: Product] {
def apply() = {
new MetadataProcessor[Metadata]{}.apply() +
new PayloadProcessor[Payload]{}.apply()
}
}
new DataProcessor[M,P]{}.apply() // "process metadataprocess payload"
However, I want to call new DataProcessor[D]{}.apply()
and gets exactly the same output. How to do it?
For an interactive Scala environment with above code, feel free to look at Scastie playground.
Upvotes: 0
Views: 57
Reputation: 51713
Try to make Metadata
and Payload
type members rather than type parameters and use type projections Data#Metadata
, Data#Payload
.
trait Data extends Product {
type Metadata <: Product
type Payload <: Product
def metadata: Metadata
def payload: Payload
}
// This is optional, just in case if you need also type parameters Metadata and Payload.
// Then you can use them as Data.Aux[Metadata, Payload]
// object Data {
// type Aux[Metadata0 <: Product, Payload0 <: Product] = Data { type Metadata = Metadata0; type Payload = Payload0 }
// }
case class M()
case class P()
case class D(metadata: M, payload: P) extends Data { type Metadata = M; type Payload = P }
trait MetadataProcessor[Metadata <: Product] {
def apply() = "process metadata"
}
trait PayloadProcessor[Payload <: Product] {
def apply() = "process payload"
}
new MetadataProcessor[M]{}.apply() // "process metadata"
new PayloadProcessor[P]{}.apply() // "process payload"
trait DataProcessor[A <: Data] {
def apply() = {
new MetadataProcessor[A#Metadata]{}.apply() +
new PayloadProcessor[A#Payload]{}.apply()
}
}
new DataProcessor[D]{}.apply() // "process metadataprocess payload"
Upvotes: 1