Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I verify type existence on compile time in Scala

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) }
                                                                                              ^
like image 408
Noam Shaish Avatar asked Jan 18 '26 21:01

Noam Shaish


1 Answers

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:

Why can't I return a concrete subtype of A if a generic subtype of A is declared as return parameter?

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
like image 92
Dmytro Mitin Avatar answered Jan 20 '26 13:01

Dmytro Mitin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!