Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Distinguish Scala-3 Enum and Sealed Traits

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
like image 465
Koosha Avatar asked Oct 12 '25 10:10

Koosha


1 Answers

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 enums)
  • 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

Update

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]
like image 177
Andrey Tyukin Avatar answered Oct 15 '25 05:10

Andrey Tyukin