Search code examples
scalareflectionscala-3

Scala 3 reflection: collect all members that are singleton objects


I have a method in Scala 2 that uses reflection. It doesn't work in Scala 3, and I would like to reimplement it so it works for Scala 3. (I will no longer need it for Scala 2 anymore, so it need not work for both).

The purpose of the method is to examine an object and collect any instances of singleton objects it has as members, in order of declaration. (It is known by the programmer ahead of time that all such singleton objects extend A).

It is working correctly.

def allSingletonObjects[A](host: Any): Vector[A] = {
 import scala.reflect.runtime.universe.runtimeMirror
 val mirror = runtimeMirror(host.getClass.getClassLoader)

 mirror
   .classSymbol(host.getClass)
   .info
   .members
   .filter(_.isModule)
   .map(sym => mirror.reflectModule(sym.asModule).instance.asInstanceOf[A])
   .toVector
   .reverse
}

Here's an an example of how it is used.

sealed trait Color

object Color {
  case object Red extends Color
  
  case object Green extends Color
  
  case object Blue extends Color
}

In this example, allSingletonObjects[Color](Color) would return Vector(Color.Red, Color.Green, Color.Blue).

How would I implement allSingletonObjects in Scala 3?

Note: I am not able to change the way I am modeling the types (e.g. using enums). I would ideally just like allSingletonObjects to work as it did in Scala 2.


Solution

  • I agree with the comments, you should probably find a better way to do this, but here is a working version:

    import scala.quoted.*
    
    inline def allSingletonObjects[A](host: Any) = allSingletonObjectsSeq[A](host).toVector
    inline def allSingletonObjectsSeq[A](host: Any) = ${allSingletonObjectsSeqImpl[A]('host)}
    
    def allSingletonObjectsSeqImpl[A: Type](host: Expr[Any])(using quotes: Quotes): Expr[Seq[A]] =
      import quotes.reflect.*
      
      Expr.ofSeq:
        host
          .asTerm
          .tpe
          .classSymbol
          .get
          .declarations
          .filter(sym => sym.flags.is(Flags.Case | Flags.Final | Flags.Lazy | Flags.Module | Flags.StableRealizable))
          .map(sym => Ref(sym).asExprOf[A])