Skip to content

Commit d847023

Browse files
committed
macro for listing typeclass instances for sealed hierarchy subtypes
1 parent 32b1c89 commit d847023

File tree

4 files changed

+28
-4
lines changed

4 files changed

+28
-4
lines changed

commons-core/src/main/scala/com/avsystem/commons/misc/SealedUtils.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ object SealedUtils {
1212
*/
1313
@explicitGenerics
1414
def caseObjectsFor[T]: List[T] = macro macros.misc.SealedMacros.caseObjectsFor[T]
15+
16+
/**
17+
* Infers a list of instances of given typeclass `TC` for all non-abstract subtypes of a sealed hierarchy root `T`.
18+
*/
19+
@explicitGenerics
20+
def instancesFor[TC[_], T]: List[TC[_ <: T]] = macro macros.misc.SealedMacros.instancesFor[TC[_], T]
1521
}
1622

1723
/**

commons-core/src/test/scala/com/avsystem/commons/misc/SealedEnumTest.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ class SealedEnumTest extends FunSuite {
1212
case object Fourth extends SomeEnum
1313

1414
val values: List[SomeEnum] = caseObjects
15+
val classTags: List[ClassTag[_ <: SomeEnum]] = SealedUtils.instancesFor[ClassTag, SomeEnum]
1516
}
1617

17-
test("case objects listing test") {
18+
test("case objects listing") {
1819
import SomeEnum._
1920
assert(values == List(First, Second, Third, Fourth))
2021
}
22+
23+
test("typeclass instance listing") {
24+
import SomeEnum._
25+
assert(classTags.map(_.runtimeClass) == List(First.getClass, Second.getClass, Third.getClass, Fourth.getClass))
26+
}
2127
}

commons-macros/src/main/scala/com/avsystem/commons/macros/MacroCommons.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,9 @@ trait MacroCommons { bundle =>
806806
case _ => dtpe.typeSymbol
807807
}
808808
Option(tpeSym).filter(isSealedHierarchyRoot).map { sym =>
809-
knownNonAbstractSubclasses(sym).toList.flatMap { subSym =>
809+
val subclasses = knownNonAbstractSubclasses(sym).toList
810+
val sortedSubclasses = if (tpeSym.pos != NoPosition) subclasses.sortBy(_.pos.point) else subclasses
811+
sortedSubclasses.flatMap { subSym =>
810812
val undetparams = subSym.asType.typeParams
811813
val undetSubTpe = typeOfTypeSymbol(subSym.asType)
812814

commons-macros/src/main/scala/com/avsystem/commons/macros/misc/SealedMacros.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ class SealedMacros(ctx: blackbox.Context) extends AbstractMacroCommons(ctx) {
99

1010
import c.universe._
1111

12-
val OrderedEnumType = getType(tq"$CommonsPkg.misc.OrderedEnum")
12+
val OrderedEnumType: Type = getType(tq"$CommonsPkg.misc.OrderedEnum")
1313

14-
def caseObjectsFor[T: c.WeakTypeTag]: Tree = {
14+
def caseObjectsFor[T: WeakTypeTag]: Tree = {
1515
val tpe = weakTypeOf[T]
1616
knownSubtypes(tpe).map { subtypes =>
1717
val objects = subtypes.map(subTpe => singleValueFor(subTpe)
@@ -20,4 +20,14 @@ class SealedMacros(ctx: blackbox.Context) extends AbstractMacroCommons(ctx) {
2020
if (tpe <:< OrderedEnumType) q"$result.sorted" else result
2121
}.getOrElse(abort(s"$tpe is not a sealed trait or class"))
2222
}
23+
24+
def instancesFor[TC: WeakTypeTag, T: WeakTypeTag]: Tree = {
25+
val tpe = weakTypeOf[T]
26+
def instanceTpe(forTpe: Type): Type = weakTypeOf[TC] match {
27+
case TypeRef(pre, sym, Nil) => internal.typeRef(pre, sym, List(forTpe))
28+
case _ => abort(s"expected type constructor")
29+
}
30+
val subtypes = knownSubtypes(tpe).getOrElse(abort(s"$tpe is not a sealed hierarchy root"))
31+
q"${subtypes.map(st => q"""$ImplicitsObj.infer[${instanceTpe(st)}]("")""")}"
32+
}
2333
}

0 commit comments

Comments
 (0)