Consider the following implementation sketch:
sealed trait Type
object Type {
case object Type1 extends Type
case object Type2 extends Type
}
sealed trait Data {
type T <: Type
}
object Data {
type Aux[TT] = Data {
type T = TT
}
case class Data1(i: Int) extends Data {
type T = Type1.type
}
case class Data2(s: String) extends Data {
type T = Type2.type
}
}
case class Main(
//lots of other fields
data: Data.Aux[T] forSome { type T <: Type}
)
// This method is supposed to accept the only Main's
// that have data of type Data.Aux[Type2.type]
def handleMainType2(
main: Main
): Unit = ???
The problem:
Having the case class containing a field of an existential type is it possible to implement a method that would accept the only branch of the existential type.
Maybe shapeless could be helpful here?
First of all,
Data.Aux[T] forSome { type T <: Type}can be written asData.Aux[_]and is justDataIf you put
printlninsidehandleMainType2then
will print one of the following (depending on how you define the type of
Mainparameter:Data.Aux[T] forSome { type T <: Type},Data.Aux[_]orData)both times. So inside method
handleMainType2main.datahas type justDataandData1/Data2are indistinguishable by type. What is distinguishable is runtime class:So you can define
with runtime behavior.
If you want compile-time behavior then you can try to make
handleMainType2a macro and use runtime reflection in the macroYou can even make the macro implicit if you prefer not to make
handleMainType2a macro itself.Notice that
handleMainType2(Main(Data2("a")))or evenwill not work.