def isSealed: Boolean
Does this symbol represent a sealed class?
Definition Classes ClassSymbolApi
google “scala 3 mirror”. but enumerating subclasses using that mirrors is a compile-time operation and requires you to provide the root type statically. if you want a dynamic (i.e. not a statically provided type) and/or runtime solution then you’ll probably need classpath scanning and that is a pandora’s box.
well, that sounds you want to have a plain old java enum
It’s not me choosing the values. The values will be given, and my function needs to determine whether they exhaust a class or not. For example, given the class classOf[java.lang.Boolean] and the values, true and false , it should determine that these given values exhaust the given class.
I’d think the best you could do is to come up with an Enumerable type class and implement this directly for the types of interest.
You could go with some macro based or reflection based approach for sealed traits with object children and somehow integrate this with the type class. You might even succeed extending this to case class children with Enumerable members, but this almost certainly will already require more of your life time than you’re willing to devote. And even then, you’d only have covered a very limited set of special cases. Most types will have an infinite domain, anyway, and of the few remaining types, the domain size will still exceed available memory.
In short, don’t go there. As @tarsa put it: Pandora’s box, whatever approach you choose.
“types of interest” ~ all types you require to support this behavior.
trait Enumerable[T]:
def allValues: List[T]
given Enumerable[Boolean] with
def allValues: List[Boolean] = List(true, false)
enum Color:
case Red, Green, Blue
given Enumerable[Color] with
def allValues: List[Color] = Color.values.toList
Starting with this, you could come up with some optional/add-on reflection/macro magic to generate the boilerplate for selected specific cases.
Exactly. So they’d need to declare the corresponding Enumerable instances themselves, potentially perusing the reflection/macro machinery you provide if they’re lucky and it matches their case. But in general we’re back to “don’t go there”.
that is for instances of classes. Answering the question, is this a list of all the possible instances of a class is impossible, except in the case of Boolean/true/false.
However, the other question is less difficult, right? Whether a class is sealed and if so whether the given set of classes exhausts the set of instantiatable subclasses.
import scala.reflect.macros.blackbox._
import scala.language.experimental.macros
def enumerateSubtypesImpl[T : c.WeakTypeTag](c: Context): c.Expr[Set[String]] = {
import c.universe._
val sym = c.mirror.weakTypeOf[T].typeSymbol
if (sym.isClass) {
val classSym = sym.asClass
if (classSym.isSealed) {
val subcs = classSym.knownDirectSubclasses.map(_.fullName)
val setApply = Select(reify(Set).tree, TermName("apply"))
c.Expr[Set[String]](Apply(setApply, subcs.toList.map(n => Literal(Constant(n)))))
}
else
c.abort(c.enclosingPosition, s"not sealed: $classSym")
}
else
c.abort(c.enclosingPosition, s"not a class: $sym")
}
def enumerateSubtypes[T]: Set[String] = macro enumerateSubtypesImpl[T]
// in another project(!):
//println(enumerateSubtypes[Boolean]) // compile error: "not sealed: class Boolean"
println(enumerateSubtypes[Color]) // Set(foo.Color.Red, foo.Color.Blue, foo.Color.Yellow)
However, this only covers a small subset of cases, I wouldn’t bet on this being foolproof, reflection/macros are complex, the closed world assumption to some extent applies in both scenarios, Strings probably won’t suffice, so you’ll either have to come up with your own types API or let reflection level types leak into the code, things likely work completely different in Scala 3, and so on. It’s a rabbit hole.
For Scala 3, you could try https://github.com/gzoller/scala-reflection. If you create an RType for sealed traits, you will get a subclass called SealedTraitInfo and that exposes the ‘children’ classes.