Every now an then i cannot understand the type logic of Scala 3. Suppose i have (simplified as much as i could from my lib):
trait Actor :
type Accept <: Actor
object Actor :
type Parent[B <: Boolean] = Actor with Child[B]
trait Child[B <: Boolean] :
type ChildAccept <: Actor
type ChildActor <: Actor = B match
case true => Actor { type Accept >: ChildAccept }
case false => Actor
def reject(child: ChildActor): Unit = ()
trait Select[B <: Boolean, LParent <: Actor.Parent[B]] :
self: Actor =>
type Parent <: Actor.Parent[B] = B match
case true => LParent { type ChildAccept <: self.Accept }
case false => LParent
def parent: Parent
Now i can define two situations which compile just fine:
trait Leaf_True[Parent <: Actor.Parent[true]] extends Select[true,Parent] :
self: Actor =>
def abandon(): Unit = parent.reject(self)
trait Leaf_False[Parent <: Actor.Parent[false]] extends Select[false,Parent] :
self: Actor =>
def abandon(): Unit = parent.reject(self)
but i cannot define the generic situation:
trait Leaf_Free[B <: Boolean, Parent <: Actor.Parent[B]] extends Select[B,Parent] :
self: Actor =>
def abandon(): Unit = parent.reject(self)
this gives:
[error] 336 | def abandon(): Unit = parent.reject(self)
[error] | ^^^^
[error] |Found: (Leaf_Free.this : Actor & Leaf_Free[B, Parent])
[error] |Required: ?1.ChildActor
[error] |
[error] |where: ?1 is an unknown value of type Leaf_Free.this.Parent²
[error] | B is a type in trait Leaf_Free with bounds <: Boolean
[error] | Parent is a type in trait Leaf_Free with bounds <: Actor.Parent³[B]
why is that? Every possible type value of B
works. Of course, you should not have the two parameters different from each other. Or should you also consider Nothing
as a separate subtype? (extending appropriately did not help). Is there a way to overcome this somehow? I would not like to write out every case manually with different traits. Not very DRY.
BTW, i also tested this in 3.3.0-RC6 but this does not make a difference. Thanks for any insights here.