Why did you make such conclusion?
Well, if you let the compiler explain itself you can observe that the match type was not reduced:
[error] | Tree: new YC()
[error] | I tried to show that
[error] | YC
[error] | conforms to
[error] | Test2.this.Z
[error] | but the comparison trace ended with `false`:
[error] |
[error] | ==> YC <: Test2.this.Z
[error] | ==> YC <: That match { case Other => XC case Any => YC } <: C (right is approximated)
[error] | <== YC <: That match { case Other => XC case Any => YC } <: C (right is approximated) = false
[error] | <== YC <: Test2.this.Z = false
as these are the rules for match reduction:
The compiler implements the following reduction algorithm:
* If the scrutinee type S is an empty set of values (such as Nothing or String & Int), do not reduce.
* Sequentially consider each pattern Pi
a. If S <: Pi reduce to Ti.
b. Otherwise, try constructing a proof that S and Pi are disjoint, or, in other words, that no value s of type S is also of type Pi.
If such proof is found, proceed to the next case (Pi+1), otherwise, do not reduce.
Disjointness proofs rely on the following properties of Scala types:
1. Single inheritance of classes
2. Final classes cannot be extended
3. Constant types with distinct values are nonintersecting
4. Singleton paths to distinct values are nonintersecting, such as object definitions or singleton enum cases.
Since (a) is clearly not the case, only (b) can cause reduction. And it looks like That
and Other
indeed do not supply enough evidence for any of (1) to (4) to be conclusive.
As @sangamon told, you should add the upper bound to the match type.
I interpreted his reply somewhat different but it makes sense, as long as you still provide enough evidence for the compiler to start the reduction. For example with literal types:
trait C { def show: Unit }
class XC extends C { def show = println("XC") }
class YC extends C { def show = println("YC") }
trait Top :
type Z <: C
trait Worker[X <: XC,Y <: YC, Q <: Boolean] extends Top :
type Z <: C = Q match
case true => X
case false => Y
class Test1() extends Worker[XC,YC,true] :
val z: Z = new XC
class Test2() extends Worker[XC,YC,false] :
val z: Z = new YC
Test1().z.show
Test2().z.show
or with object types:
trait C { def show: Unit }
class XC extends C { def show = println("XC") }
class YC extends C { def show = println("YC") }
trait Top:
type Z <: C
sealed trait Selector
case object Other extends Selector
case object That extends Selector
trait Worker[X <: XC, Y <: YC, Q <: Selector] extends Top:
type Z <: C = Q match
case Other.type => X
case That.type => Y
class Test1() extends Worker[XC, YC, Other.type]:
val z: Z = new XC
class Test2() extends Worker[XC, YC, That.type]:
val z: Z = new YC
Test1().z.show
Test2().z.show
But i think we have it all clear now. Again, thx for the help, i learned that matching a type is not the same as a match type. 