yiksanchan
yiksanchan

Reputation: 1940

Get sub type parameter from a type parameter

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

Answers (1)

Dmytro Mitin
Dmytro Mitin

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

Related Questions