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)?
The only way I have figured out how to get this to work is to extract the type in a pattern match:
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.