Fully parametric polymorphic functions exist in Scala 3. In Scala 2 there are no parametric polymorphic functions (and polymorphic values at all, only polymorphic methods) but there are ad-hoc polymorphic functions implemented normally with Shapeless.
I guess in Shapeless there is a type class (Generic
/LabelledGeneric
) for iterating case classes extending some sealed trait
case class Bar1() extends MyTrait
//...
case class Bar100() extends MyTrait
Scala how to derivate a type class on a trait
Use the lowest subtype in a typeclass?
Type class instance for case objects defined in sealed trait
Iteration over a sealed trait in Scala?
Getting subclasses of a sealed trait
Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?
but not for iterating case classes nested into an object. So probably we'd need a macro (compile-time reflection) anyway, even using Shapeless
import scala.language.experimental.macros
// libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value
import scala.reflect.macros.whitebox
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.HList
trait GetInnerCaseClasses[A] {
type Out <: HList
}
object GetInnerCaseClasses {
type Aux[A, Out0] = GetInnerCaseClasses[A] { type Out = Out0 }
implicit def mkGetInnerCaseClasses[A, Out <: HList]: Aux[A, Out] =
macro mkGetInnerCaseClassesImpl[A]
def mkGetInnerCaseClassesImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val A = weakTypeOf[A]
val caseClasses = A.decls.filter(s => s.isClass && s.asClass.isCaseClass)
val hList = caseClasses.foldRight[Tree](tq"_root_.shapeless.HNil")(
(s, hl) => tq"_root_.shapeless.::[$s, $hl]"
)
q"""
new GetInnerCaseClasses[$A] {
override type Out = $hList
}
"""
}
}
// in a different subproject
import shapeless.ops.hlist.{FillWith, Mapper}
import shapeless.{::, HList, HNil, Poly0, Poly1, Typeable, the}
object Foo {
case class Bar1()
// ...
case class Bar100()
}
implicitly[GetInnerCaseClasses.Aux[Foo.type, Bar1 :: Bar2 :: Bar100 :: HNil]] // compiles
val gicc = the[GetInnerCaseClasses[Foo.type]] // "the" is an advanced version of "implicitly" not damaging type refinements
implicitly[gicc.Out =:= (Bar1 :: Bar2 :: Bar100 :: HNil)] // compiles
// I'm just printing names of case classes, you should replace this with your actual iterating logic
object myPoly extends Poly1 {
implicit def cse[A <: Product : Typeable]: Case.Aux[A, Unit] =
at(_ => println(Typeable[A].describe))
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def myIterate[A <: Singleton] = new PartiallyAppliedMyIterate[A]
class PartiallyAppliedMyIterate[A <: Singleton] {
def apply[L <: HList]()(implicit
getInnerCaseClasses: GetInnerCaseClasses.Aux[A, L],
mapper: Mapper[myPoly.type, L],
fillWith: FillWith[nullPoly.type, L] // because type class GetInnerCaseClasses works only on type level
): Unit = mapper(fillWith())
}
myIterate[Foo.type]()
// Bar1
// ...
// Bar100