Scala 宏:在 class 定义中获取对象成员的根类型的类型参数
Scala macros: Get type arguments of root type of object members inside class definition
有点奇怪的场景,最好在下面概述。
假设我有 sealed trait Painting[T]
,然后是一堆更专业的变体,例如 trait ImpressionistPainting[T] extends Painting[T]
等等,构建一个简单的密封类型家族。
然后我有一个 class 具有 f 有界多态类型绑定:
class Gallery[T <: Gallery[T]]()(implicit helper: GalleryHelper[T])
还有一个:
trait GalleryHelper[T <: Gallery[T]] {
def paintings: Set[Painting[_]]
}
object GalleryHelper {
implicit def materialize[T <: Gallery[T]]: GalleyHelper[T] = {
macro MyMacro.materialize[T]
}
def apply[T <: Gallery[T]]()(
implicit ev: GalleryHelper[T]
): GalleryHelper[T] = ev
}
到目前为止,这是非常基本和直接的 foo 条码。
此设置的整个目标是宏观具体化我需要描述画廊的项目的帮助列表,或者在这种情况下任意键入 "user content",例如:
class MyGallery extends Gallery[MyGallery] {
object `le-reve` extends CubistPainting[Picasso]
object starry_night extends PostImpressionistPainting[VanGogh]
// ..
}
现在有了一些宏兼容的乐趣,我想宏具体化有问题的助手并过滤 T <: Gallery[T]
的成员以提取 MemberType <:< Painting[_]
.
的成员
一切都非常简单,下面比简单的 type.decls.filter(_.typeSignature <:< typeOf[Filter]
更复杂,因为它需要 return 所有继承成员的列表,按照它们的写入顺序,给定画廊扩展其他画廊例如。
def findMembersWithBound[T : WeakTypeTag, Filter : TypeTag](
exclusions: Symbol => Option[Symbol] = { s: Symbol => Some(s) }
): Set[Symbol] = {
val tpe = weakTypeOf[T].typeSymbol.typeSignature
(
for {
baseClass <- tpe.baseClasses.reverse.flatMap(exclusions(_))
symbol <- baseClass.typeSignature.members.sorted
if symbol.typeSignature <:< typeOf[Filter]
} yield symbol
)(collection.breakOut)
}
因此在隐式宏中,类型 T 的模块成员的基本遍历需要按子类型进行过滤,在本例中为 Painting[_]
,然后查看扩展 Painting
变体时的用户。类型族是密封的,因此用户使用 object
扩展 Painting
的子 class,永远不要直接 Painting[_]
,如果这与任何方式相关。
@macrocompat.bundle
class MyMacro(val c: blackbox.Context) {
import c.universe._
def materialize[T <: Gallery[T]]: Tree = {
val galleryTpe = weakTypeOf[T]
val fields = findMembersWithBound[T, Painting[_]](exclusions)
val colMembers = sourceMembers.map { member =>
val memberType = member.typeSignatureIn(galleryTpe)
memberType.baseClasses.find(colSymbol ==) match {
case Some(root) => {
// Herein lies the problem, typeArgs is Nil.
root.typeSignatureIn(memberType).typeArgs.headOption match {
case Some(colSignature) => colSignature
case None => c.abort(
c.enclosingPosition,
s"Could not find the artist for ${member.asModule.name}"
)
}
}
case None => c.abort(c.enclosingPosition, s"Could not find root painting type for ${member.asModule.name}")
}
}
}
问题是传递给 Painting
的原始类型参数中的 none 不再可见,即使 typeSignature
应该在范围内进行评估等等,我只是想确保梵高不会成为众所周知的沃尔多。
什么是正确的 API 到 dealias
或者如何让那些 typeArgs
再次可见?当前为空列表。
好的,原来有一种 "well hidden" 方法可以使用 asSeenFrom
来做到这一点。相关部分是:
root.typeSignature.typeParams match {
case head :: Nil => head.asType.toType.asSeenFrom(memberType, colSymbol)
case _ => c.abort(
c.enclosingPosition,
"Expected exactly one type parameter provided for painting type"
)
}
有点奇怪的场景,最好在下面概述。
假设我有 sealed trait Painting[T]
,然后是一堆更专业的变体,例如 trait ImpressionistPainting[T] extends Painting[T]
等等,构建一个简单的密封类型家族。
然后我有一个 class 具有 f 有界多态类型绑定:
class Gallery[T <: Gallery[T]]()(implicit helper: GalleryHelper[T])
还有一个:
trait GalleryHelper[T <: Gallery[T]] {
def paintings: Set[Painting[_]]
}
object GalleryHelper {
implicit def materialize[T <: Gallery[T]]: GalleyHelper[T] = {
macro MyMacro.materialize[T]
}
def apply[T <: Gallery[T]]()(
implicit ev: GalleryHelper[T]
): GalleryHelper[T] = ev
}
到目前为止,这是非常基本和直接的 foo 条码。
此设置的整个目标是宏观具体化我需要描述画廊的项目的帮助列表,或者在这种情况下任意键入 "user content",例如:
class MyGallery extends Gallery[MyGallery] {
object `le-reve` extends CubistPainting[Picasso]
object starry_night extends PostImpressionistPainting[VanGogh]
// ..
}
现在有了一些宏兼容的乐趣,我想宏具体化有问题的助手并过滤 T <: Gallery[T]
的成员以提取 MemberType <:< Painting[_]
.
一切都非常简单,下面比简单的 type.decls.filter(_.typeSignature <:< typeOf[Filter]
更复杂,因为它需要 return 所有继承成员的列表,按照它们的写入顺序,给定画廊扩展其他画廊例如。
def findMembersWithBound[T : WeakTypeTag, Filter : TypeTag](
exclusions: Symbol => Option[Symbol] = { s: Symbol => Some(s) }
): Set[Symbol] = {
val tpe = weakTypeOf[T].typeSymbol.typeSignature
(
for {
baseClass <- tpe.baseClasses.reverse.flatMap(exclusions(_))
symbol <- baseClass.typeSignature.members.sorted
if symbol.typeSignature <:< typeOf[Filter]
} yield symbol
)(collection.breakOut)
}
因此在隐式宏中,类型 T 的模块成员的基本遍历需要按子类型进行过滤,在本例中为 Painting[_]
,然后查看扩展 Painting
变体时的用户。类型族是密封的,因此用户使用 object
扩展 Painting
的子 class,永远不要直接 Painting[_]
,如果这与任何方式相关。
@macrocompat.bundle
class MyMacro(val c: blackbox.Context) {
import c.universe._
def materialize[T <: Gallery[T]]: Tree = {
val galleryTpe = weakTypeOf[T]
val fields = findMembersWithBound[T, Painting[_]](exclusions)
val colMembers = sourceMembers.map { member =>
val memberType = member.typeSignatureIn(galleryTpe)
memberType.baseClasses.find(colSymbol ==) match {
case Some(root) => {
// Herein lies the problem, typeArgs is Nil.
root.typeSignatureIn(memberType).typeArgs.headOption match {
case Some(colSignature) => colSignature
case None => c.abort(
c.enclosingPosition,
s"Could not find the artist for ${member.asModule.name}"
)
}
}
case None => c.abort(c.enclosingPosition, s"Could not find root painting type for ${member.asModule.name}")
}
}
}
问题是传递给 Painting
的原始类型参数中的 none 不再可见,即使 typeSignature
应该在范围内进行评估等等,我只是想确保梵高不会成为众所周知的沃尔多。
什么是正确的 API 到 dealias
或者如何让那些 typeArgs
再次可见?当前为空列表。
好的,原来有一种 "well hidden" 方法可以使用 asSeenFrom
来做到这一点。相关部分是:
root.typeSignature.typeParams match {
case head :: Nil => head.asType.toType.asSeenFrom(memberType, colSymbol)
case _ => c.abort(
c.enclosingPosition,
"Expected exactly one type parameter provided for painting type"
)
}