I have the following traits and classes:
sealed trait Signal
sealed trait Description[T]
final case class S1(name: String) extends Signal
final case class D1(name: String) extends Description[S1]
What I try to achieve is that anyone who wants to add Signal will have (at compile time) to create a description.
I don't want to change the signature of Description
but for sure not of Signal
I set my compiler to fail on warning, so I can leverage the fact that my ADT is sealed.
My idea was to have such a "compilation guard":
def compilationGuard[S <: Signal](s: S): Description[S] = s match { case S1(name) => D1(name) }
but I get the following error:
<console>:17: error: type mismatch;
found : D1
required: Description[S]
def compilationGuard[S <: Signal](s: S): Description[S] = s match { case S1(name) => D1(name) }
^
def compilationGuard[S <: Signal](s: S): Description[S] = s match { case S1(name) => D1(name) }
can't compile for the same reason as
def returnItself[S <: Signal](s: S): S = s match { case S1(name) => S1(name) }
Reasons are explained here in details:
Type mismatch on abstract type used in pattern matching
If you don't want to mix Description
logic to ADT or define instances of a type class like SignalMapper
manually you can use Shapeless
import shapeless.ops.coproduct.Mapper
import shapeless.{:+:, CNil, Coproduct, Generic, Poly1}
def compilationGuard[C <: Coproduct]()(implicit
gen: Generic.Aux[Signal, C],
mapper: Mapper[uniqueDescriptionPoly.type, C]
) = null
object uniqueDescriptionPoly extends Poly1 {
implicit def cse[S <: Signal, C1 <: Coproduct](implicit
gen1: Generic.Aux[Description[S], C1],
ev: C1 <:< (_ :+: CNil)
): Case.Aux[S, Null] = null
}
compilationGuard()
Testing:
final case class S1(name: String) extends Signal
final case class S2(name: String) extends Signal
final case class D1(name: String) extends Description[S1]
// doesn't compile
final case class S1(name: String) extends Signal
final case class S2(name: String) extends Signal
final case class D1(name: String) extends Description[S1]
final case class D2(name: String) extends Description[S1]
// doesn't compile
final case class S1(name: String) extends Signal
final case class S2(name: String) extends Signal
final case class D1(name: String) extends Description[S1]
final case class D2(name: String) extends Description[S2]
// compiles