Summary:
Using an object which contains both a sealed trait
and all of its implementers, how can obtain the set of the class names of all the sealed trait
implementers using the the containing object's getClass
method (as opposed to using a type parameter)?
Details:
I am currently using Scala 2.11.2 and am attempting to obtain the set of descendants from a sealed trait
. I have seen Travis Brown's solution. However, it uses a type parameter of Root: TypeTag
. I need to be able to seed the scan by dynamically discovering the Symbol
at runtime via an object
instance's getClass
method.
Consider this example class:
object SealedTest {
sealed trait T
case object A extends T
case object B extends T
}
My goal is to have a function outside of object SealedTest
which looks like this:
def getSealedTraitImplementers(clazz: Class[_]): Set[String]
Calling getSealedTraitImplementers(SealedTest.T.getClass)
would return Set(A, B)
. And I do get that obtaining the class for SealedTest.T
is a bit more involved than this over simplified example (actual full details are in the val sealedDescendantsViaClassInstance
below).
Here's what I have tried thus far:
Using Travis's code as a starting point, I created this function:
def inspect(symbol: Symbol): Option[Set[Symbol]] = {
val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
if (internal.isSealed)
Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol)
else None
}
I then take his original primary entry point and defined it like this:
def sealedDescendants[Root: TypeTag]: Option[Set[Symbol]] = {
val symbol = typeOf[Root].typeSymbol
inspect(symbol)
}
And this succeeds in returning Set(object A, object B)
when called like this:
val resultSealedDescendants =
sealedDescendants[SealedTest.T] match {
case Some(set) => set.toString
case None => "<empty>"
}
println(s"sealedDescendants[SealedTest.T]=${resultSealedDescendants}")
While it isn't a Set[String]
, it's trivial to get there with resultSealedDescendants.map(_.toString.stripPrefix("object ")
.
Now, for what I haven't been able to get to work. I define this function to obtain the Symbol
from the existing runtime class instance of SealedTest.T
:
def sealedDescendantsViaClassInstance: Option[Set[Symbol]] = {
val classSealedTestT = Class.forName(SealedTest.getClass.getName + "T")
val runtimeMirrorTemp = runtimeMirror(classSealedTestT.getClassLoader)
val classSymbol = runtimeMirrorTemp.staticClass(classSealedTestT.getName)
//???missing transform of ClassSymbol to Symbol???
inspect(classSymbol)
}
And this fails in returning <empty>
when called like this:
val resultSealedDescendantsViaClassInstance =
sealedDescendantsViaClassInstance match {
case Some(set) => set.toString
case None => "<empty>"
}
println(s"sealedDescendantsViaClassInstance=$resultSealedDescendantsViaClassInstance")
I am clearly obtaining a Symbol
(ClassSymbol
to be more precise). However, the inspect
function is not returning a Set
. So, I think there is still some transformation I need to do on classSymbol
before calling inspect
. What is the missing transform?
UPDATE:
After spending hours trying to close this gap, I gave up and modified the original object to add an (ugly) explicit type class. This is very much not the desired solution. The object now looks like this:
object SealedTest {
sealed trait T
case object A extends T
case object B extends T
def typeTagT: TypeTag[_] = typeTag[SealedTest.T]
}
I still have the need for the original requirement to be able to start at the getClass
for T
. So, if you have any clue how I can start from there to dynamically constitute a type class which can produce the needed Symbol for sealed trait T
, I would really appreciate it. I will post an answer if I end up figuring it out.