Skip to content

Instantly share code, notes, and snippets.

@abhandaru
Last active February 6, 2020 09:10
Show Gist options
  • Save abhandaru/b540f8f917b476b1b5ef7267c262e1d2 to your computer and use it in GitHub Desktop.
Save abhandaru/b540f8f917b476b1b5ef7267c262e1d2 to your computer and use it in GitHub Desktop.
import language.experimental.macros
import scala.reflect.macros.whitebox.Context
// Macro for iterating sealed trait enums
// https://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala
object SealedEnum {
def values[A]: Set[A] = macro values_impl[A]
def values_impl[A: c.WeakTypeTag](c: Context) = {
import c.universe._
val symbol = weakTypeOf[A].typeSymbol
if (!symbol.isClass) c.abort(
c.enclosingPosition,
"Can only enumerate values of a sealed trait or class."
) else if (!symbol.asClass.isSealed) c.abort(
c.enclosingPosition,
"Can only enumerate values of a sealed trait or class."
) else {
val tpe = symbol.asClass
tpe.typeSignature // needed because of scala core bug: https://github.com/scala/bug/issues/7588
val children = tpe.knownDirectSubclasses.toList
if (!children.forall(_.isModuleClass)) c.abort(
c.enclosingPosition,
"All children must be objects."
) else c.Expr[Set[A]] {
def sourceModuleRef(sym: Symbol) = Ident(
sym.asInstanceOf[
scala.reflect.internal.Symbols#Symbol
].sourceModule.asInstanceOf[Symbol]
)
Apply(
Select(
reify(Set).tree,
TermName("apply")
),
children.map(sourceModuleRef(_))
)
}
}
}
}
import org.scalatest.{Matchers, WordSpec}
sealed class TestSealedClass(key: String)
object TestSealedClass {
object ClassA extends TestSealedClass("A")
object ClassB extends TestSealedClass("B")
object ClassC extends TestSealedClass("C")
}
sealed trait TestSealedTrait
object TestSealedTrait {
object ClassA extends TestSealedTrait
object ClassB extends TestSealedTrait
object ClassC extends TestSealedTrait
}
class SealedEnumSpec extends WordSpec with Matchers {
"SealedEnum" should {
"enumerate sealed class correctly" in {
val values = SealedEnum.values[TestSealedClass]
values should be(Set(TestSealedClass.ClassA, TestSealedClass.ClassB, TestSealedClass.ClassC))
}
"enumerate sealed trait correctly" in {
val values = SealedEnum.values[TestSealedTrait]
values should be(Set(TestSealedTrait.ClassA, TestSealedTrait.ClassB, TestSealedTrait.ClassC))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment