Is it possible to distinguish Scala-3 enums and sealed traits using Mirrors or even Macros?
transparent inline def isScalaEnum[A]: Boolean = ${ isScalaEnumImpl[A] }
private def isScalaEnumImpl[A: Type](using q: Quotes): Expr[Boolean] = ???
For example, how do you implement the above macro?
sealed trait T
case class A(x: Int) extends T
case class B(x: String) extends T
enum Color(val rgb: Int):
case Red extends Color(1)
case Green extends Color(2)
isScalaEnum[T] should be false
isScalaEnum[Color] should be true
You could do it with <:<
and NotGiven
:
<:< reflect.Enum
for checking whether your type is subclass of scala.reflect.Enum
(which is true only for Scala 3 enum
s)NotGiven
for translating the absence of <:<
-evidence into a false
Here is how it can be implemented:
import scala.util.NotGiven
case class IsEnum[X](value: Boolean)
given isEnum[X](using X <:< reflect.Enum): IsEnum[X] = IsEnum(true)
given isNotEnum[X](using NotGiven[X <:< reflect.Enum]): IsEnum[X] = IsEnum(false)
inline def isScalaEnum[X](using inline ev: IsEnum[X]): Boolean = ev.value
Here is how it can be used:
enum Foo:
case Bar
sealed trait NotFoo
@main def demo(): Unit =
println(isScalaEnum[Foo]) // true
println(isScalaEnum[NotFoo]) // false
If you want to have more precise type information at compile time, just transparently inline all the things:
import scala.util.NotGiven
case class IsEnum[X](value: Boolean)
transparent inline given isEnum[X](using X <:< reflect.Enum): IsEnum[X] =
IsEnum(true)
transparent inline given isNotEnum[X](using NotGiven[X <:< reflect.Enum]): IsEnum[X] =
IsEnum(false)
transparent inline def isScalaEnum[X](using inline ev: IsEnum[X]): ev.value.type =
ev.value
It then behaves similarly to the test cases that you've mentioned in your own answer, with Null
and Nothing
being notable exceptions:
enum Foo:
case Bar
enum FooS(x:String):
case Bar extends FooS("str")
sealed trait NotFoo
inline val a: true = isScalaEnum[Foo]
inline val b: true = isScalaEnum[Foo.Bar.type]
inline val c: true = isScalaEnum[FooS]
inline val d: true = isScalaEnum[FooS.Bar.type]
inline val e: false = isScalaEnum[NotFoo]
inline val f: false = isScalaEnum[Int]
inline val g: false = isScalaEnum[String]
inline val x: true = isScalaEnum[Null]
inline val y: true = isScalaEnum[Nothing]
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