英文:
Recursively obtain subclass(es) of a class
问题
Main Question: 如何在Scala中递归打印一个类的所有子类?
Context:
我正在使用CDE库(https://github.com/chipsalliance/cde),该库在各种下游项目中用于参数化类。由于使用该库,会创建大量的案例类,这些案例类扩展了一个名为“Config”的类(分布在多个Scala文件中),如下所示:
class Cfg0 extends Config(...)
class Cfg1 extends Cfg0
class Cfg2 extends Cfg1
我们的用户通常会在Makefiles等地方手动指定“Config”对象的名称,我想打印所有类的名称,以便更容易发现这些类(而不是使用grep)。
“Config”类的定义:https://github.com/chipsalliance/cde/blob/384c06b8d45c8184ca2f3fba2f8e78f79d2c1b51/cde/src/chipsalliance/rocketchip/config.scala#L151
我已经查看了以下链接:
https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a-trait-without-using-t
https://gist.github.com/longshorej/1a0a2cf50de8e6ff101c
但它们似乎是面向案例类/特征的。
英文:
Main Question: How can you print all the subclasses (recursively) of a class in Scala?
Context:
I am using the CDE library (https://github.com/chipsalliance/cde) that is used in a variety of downstream projects to parameterize classes. As a result of using the library, a large amount of case classes are created extending a class called Config
(across multiple Scala files), like so:
class Cfg0 extends Config(...)
class Cfg1 extends Cfg0
class Cfg2 extends Cfg1
Our users normally manually call out the "Config" object names in Makefiles, etc and I would like to print the names of all the classes so that these classes are easier to discover (instead of grepping).
Definition of Config
class: https://github.com/chipsalliance/cde/blob/384c06b8d45c8184ca2f3fba2f8e78f79d2c1b51/cde/src/chipsalliance/rocketchip/config.scala#L151
I've looked at:
https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a-trait-without-using-t
https://gist.github.com/longshorej/1a0a2cf50de8e6ff101c
But they looked like they were oriented around case classes/traits.
答案1
得分: 0
I slightly updated my answer at https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a trait without using t
Out of the options listed there, for example at runtime you can use classpath scanners like Reflections, ClassGraph, Burningwave, ...
// libraryDependencies += "org.reflections" % "reflections" % "0.10.2"
import org.reflections.Reflections
import org.reflections.scanners.Scanners.SubTypes
import scala.jdk.CollectionConverters._
val reflections = new Reflections()
reflections.get(SubTypes.of(classOf[Config]).asClass()).asScala
// Set(class Cfg0, class Cfg1, class Cfg2)
Or at compile time you can try Shapeless and macro-based type class KnownSubclasses
from https://stackoverflow.com/questions/75544693/shapeless-how-to-derive-labelledgeneric-for-coproduct (traversing ASTs in all compilation units of a current compiler run, not using scala-reflect
.knownDirectSubclasses
)
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{Coproduct, HList, Poly0, Poly1, Typeable}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.{FillWith, Mapper, ToList}
object typeablePoly extends Poly1 {
implicit def cse[A](implicit typeable: Typeable[A]): Case.Aux[A, String] =
at(_ => typeable.describe)
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def getSubclasses[A] = new PartiallyAppliedGetSubclasses[A]
class PartiallyAppliedGetSubclasses[A] {
def apply[C <: Coproduct, L <: HList, L1 <: HList]()(implicit
knownSubclasses: KnownSubclasses.Aux[A, C],
toHList: ToHList.Aux[C, L],
mapper: Mapper.Aux[typeablePoly.type, L, L1],
fillWith: FillWith[nullPoly.type, L],
toList: ToList[L1, String]
): List[String] =
toList(mapper(fillWith()))
}
getSubclasses[Config]()
// List(Cfg0, Cfg1, Cfg2)
For both above options it's irrelevant whether traits/classes are sealed or not, whether classes are case classes or not.
英文:
I slightly updated my answer at https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a-trait-without-using-t
Out of the options listed there, for example at runtime you can use classpath scanners like Reflections, ClassGraph, Burningwave, ...
// libraryDependencies += "org.reflections" % "reflections" % "0.10.2"
import org.reflections.Reflections
import org.reflections.scanners.Scanners.SubTypes
import scala.jdk.CollectionConverters._
val reflections = new Reflections()
reflections.get(SubTypes.of(classOf[Config]).asClass()).asScala
// Set(class Cfg0, class Cfg1, class Cfg2)
Or at compile time you can try Shapeless and macro-based type class KnownSubclasses
from https://stackoverflow.com/questions/75544693/shapeless-how-to-derive-labelledgeneric-for-coproduct (traversing ASTs in all compilation units of a current compiler run, not using scala-reflect
.knownDirectSubclasses
)
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{Coproduct, HList, Poly0, Poly1, Typeable}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.{FillWith, Mapper, ToList}
object typeablePoly extends Poly1 {
implicit def cse[A](implicit typeable: Typeable[A]): Case.Aux[A, String] =
at(_ => typeable.describe)
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def getSubclasses[A] = new PartiallyAppliedGetSubclasses[A]
class PartiallyAppliedGetSubclasses[A] {
def apply[C <: Coproduct, L <: HList, L1 <: HList]()(implicit
knownSubclasses: KnownSubclasses.Aux[A, C],
toHList: ToHList.Aux[C, L],
mapper: Mapper.Aux[typeablePoly.type, L, L1],
fillWith: FillWith[nullPoly.type, L],
toList: ToList[L1, String]
): List[String] =
toList(mapper(fillWith()))
}
getSubclasses[Config]()
// List(Cfg0, Cfg1, Cfg2)
For both above options it's irrelevant whether traits/classes are sealed or not, whether classes are case classes or not.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论