I ran into another issue of scala's subtype relation being non transitive (https://github.com/scala/bug/issues/12164) and I start to think if there is some general work around possible. Long story short:
trait Thing { thisThing =>
type Super >: Self <: Thing {
type Super <: thisThing.Super
}
type Self <: Thing {
type Super = thisThing.Super
type Self = thisThing.Self
}
}
trait Wrapper extends Thing {
type SuperAdapted >: Adapted <: Thing
type Adapted <: Thing
}
trait Adapter[+T <: Thing] extends Wrapper {
val thing :T
type Super = Adapter[thing.Super]
type Self = Adapter[thing.Self]
type Adapted = thing.Self
}
type Universal[+T <: Thing] = Wrapper { type Adapted <: T }
And this does not compile:
val t :Thing
implicitly[Adapter[t.Super] <:< Universal[t.Super]]
Now I have T <: Thing { type General <: G }
but not Adapter[T] <:< Universal[G]
.
Is there a way to work around it? One would be of course to unitilize <:<#liftCo
, but it works only for values, not types, and prevents higher types from using Adapter[T]
and retaining a proper subtyping relation - basically now everywhere I'd have to switch back and forth using implicits and it's completely unmanagable. Can anyone think of an alternate definition of Universal
such that at least Adapter[t.Self] <:< Universal[t.Super]
and Adapter[t.Super] <:< Universal[t.Super]
, but preferably one which encompasses all wrappers with a.thing.Self <: T
for arbitrary T
(because I don't even have a T <: Thing
in the place where I need this subtyping to work). I don't know, type lambdas, braking it into partial type definitions to somehow manually enforce transitiveness?
What I eventually did is I reversed the dependency. Instead of
(or similar), I added more member types to
Thing
:where the type
SuperAdapted
was ommitted from the original question in minimizing, but is required in my actual problem:Now, instead of bounds
[T <: Thing, A <: Universal[T]]
and passing[t.Super, Adapter[Super]]
I have[T <: Thing, A <: T#AdaptSuper]
(or[T <: Thing, A <: T#Adapt]
, depending on the use case). It is not as flexible or elegant as before, but works and fits my use cases. That is, until Scala 3...