While cracking my head over another question, I came across different riddles which seem related. This is one of them:
trait Sys[S <: Sys[S]] {
type Peer <: Sys[Peer]
}
trait Fenced {
type Peer <: Sys[Peer]
}
def makeFence[S <: Sys[S]] = new Fenced { type Peer = S#Peer }
Where the error is as follows:
error: overriding type Peer in trait Fenced with bounds >: Nothing <: Sys[this.Peer];
type Peer has incompatible type
def makeFence[S <: Sys[S]] = new Fenced { type Peer = S#Peer }
^
Why? (also tried to add self-type _:S =>
to Sys
, didn't matter)
While Rex's answer makes it possible to construct the Fenced
object, it does not really solve the issues I have with the representation type character getting lost when using a type projection (S#Peer
). I have come up with another scenario which poses harder constraints; I think this is the core issue:
trait Test[S <: Sys[S]] {
def make[T <: Sys[T]](): Unit
make[S#Peer]()
}
error: type arguments [S#Peer] do not conform to method make's type
parameter bounds [T <: Sys[T]]
make[S#Peer]()
^
I still am not entirely certain what constraints you're looking for, but here is one possibility:
trait Sys[S <: Sys[S]] {
type Peer <: Sys[Peer]
}
trait Fenced {
type MySys <: Sys[MySys]
type Peer = MySys#Peer
}
def makeFence[S <: Sys[S]] = new Fenced{ type MySys = S }
This gives you (and requires!) access to both Peer
and the original outer type within Fenced
. I am not sure whether Fenced
may do this, or whether it must abstract across outer types.
Can you make Sys
's type parameter covariant? For example, this compiles:
trait Sys[+S <: Sys[S]] { type Peer <: Sys[Peer] }
trait Fenced { type Peer <: Sys[Peer] }
def makeFence[S <: Sys[S]] = new Fenced { type Peer = S#Peer }
Now if we have the following (wrapped in an object only for REPL copy-paste convenience):
object Example {
case class SysX(i: Int) extends Sys[SysX] { type Peer = SysY }
case class SysY(j: Int) extends Sys[SysY] { type Peer = SysX }
}
import Example._
It works as I'd expect:
scala> val fenceX = makeFence[SysX]
fenceX: java.lang.Object with Fenced{type Peer = Example.SysX#Peer} = ...
scala> val y: fenceX.Peer = SysY(1)
y: fenceX.Peer = SysY(1)
scala> val y: fenceX.Peer = SysX(1)
<console>:15: error: type mismatch;
found : Example.SysX
required: fenceX.Peer
val y: fenceX.Peer = SysX(1)
Which (I think) is what you want?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With