How to recover bounds on wildcards in Scala 2?

21 Views Asked by At

Consider the following code:

trait Aggregate[T <: Aggregate[T]] {
  type B = T
  def split(percentage: Double): (T, T)
  def add(other: T): T
}

case class Scoria(total: Int) extends Aggregate[Scoria] {
  override def split(percentage: Double): (Scoria, Scoria) = {
    val amount = (total * (percentage / 100)).toInt
    (Scoria(total - amount), Scoria(amount))
  }

  override def add(other: Scoria): Scoria = copy(total = total + other.total)
}


trait Transporter[T <: Aggregate[T]] {
  def load(aggregate: T): Transporter[T]
}

case class Truck[T <: Aggregate[T]](aggregate: T) extends Transporter[T] {
  override def load(aggregate: T): Transporter[T] = Truck(this.aggregate.add(aggregate))
}


case class SiteProps[T <: Aggregate[T]](aggregate: T, transporter: Transporter[T]) {
  type A = T
  type C = T#B
}


trait Site {

  val props: SiteProps[_]

  private def runWith[T <: Aggregate[T]](props: SiteProps[T]): Unit = {
      val (remaining, going) = props.aggregate.split(50D)
      val loadedTransporter = props.transporter.load(going)
      println(loadedTransporter)
  }

  def run(): Unit = {
    runWith(props)
  }
}


val site = new Site {
  override val props: SiteProps[Scoria] = SiteProps(Scoria(3), Truck[Scoria](Scoria(0)))
}

site.run()

In Scala 3 this compiles and runs fine.

In Scala 2 it fails to compile with:

    runWith(props)
    ^
<pastie>:49: error: inferred type arguments [_$1] do not conform to method runWith's type parameter bounds [T <: Aggregate[T]]

    runWith(props)
            ^
<pastie>:49: error: type mismatch;
 found   : SiteProps[_$1] where type _$1
 required: SiteProps[T]

I have tried these variations with no luck:

    runWith(props.asInstanceOf[SiteProps[props.A]])
    runWith(props.asInstanceOf[SiteProps[props.A#B]])
    runWith(props.asInstanceOf[SiteProps[props.C]])

Is there any way to get this to compile (even with a cast)?

1

There are 1 best solutions below

0
steinybot On

The only way I have figured out how to get this to work is to extract the type in a pattern match:

    props match {
      case p: SiteProps[t] => runWith[t](p)
    }

The problem with this is that you have to do it everywhere that you use it or have some other way to keep a hold of t.