问题描述
让X
是具有类型参数A1
,A2
,...,An
的类型构造函数。例如Option[A]
和Function1[A1,A2]
。
让X[T1,T2,...,Tn]
为通过将类型构造函数X
应用于具体的类型自变量T1
,T2
,... Tn
而得到的类型。例如Option[Int]
和Function1[Long,List[String]]
。
让Y
是X
的直接子类,它使X
的某些类型参数不固定,不添加新的自由类型参数,并且其类型参数为{{1 }},B1
,...,B2
和Bm
。例如m <= n
和Some[B]
。
我需要实现一个函数,该函数找出要分配给类型参数PartialFunction[B1,B2]
,{的具体类型R1
,R2
,...,Rm
类型构造函数B1
的{1}},...,B2
,这样Bm
的所有差异均被删除(所有特征和类均视为非变异)。
对于Y
和Y[R1,R2,Rm] <:< X[T1,Tn]
,很明显Option
对于Some
的{{1}}除去了方差是正确的。
同样,在R1 = T1
和Some[R1] <:< Option[T1]
的情况下,显然Function1
的{{1}}和PartialFunction
的方差已删除。
但是对于一般情况,情况要复杂一些。
鉴于编译器和反射库的R1 = T1
运算符都必须解决此问题才能检查可分配性;我认为反射API的功能已经解决了我的问题。但我找不到。
是的,它应该是R2 = T2
,但是我没有用。当然,我做错了。
PartialFunction[R1,R2] <:< Function[T1,T2]
<:<
是回答此问题的函数。
def asSeenFrom(pre: Type,clazz: Symbol): Type
scala版本:2.13.3
解决方法
这是部分答案。它仅找出基类X
的类型参数与直接子类Y
的类型参数之间的关系。
import scala.reflect.runtime.universe._
/** @param baseType a type resulting of the instantiation of a type constructor. For example: {{{typeOf[Option[Int]]}}}
* @param directSubclassTypeConstructor the type constructor whose type parameters we want to instantiate. It should be a subclass of the `baseType`'s type constructor.
* @return the relationship between `baseType`'s type arguments and `directSubclassTypeconstructor`'s type parameters. For example: {{{Map(A -> Int)}}}*/
def typeParametersToBaseTypeArgumentsRelationship(baseType: Type,directSubclassTypeConstructor: Type): Map[Type,Type] = {
val baseTypeConstructor = baseType.typeConstructor;
assert(directSubclassTypeConstructor <:< baseTypeConstructor)
val typeParamsRelationship =
for {
(baseTypeParam,baseTypeArgument) <- baseTypeConstructor.typeParams zip baseType.typeArgs
} yield {
val directSubclassTypeParam = baseTypeParam.asType.toType.asSeenFrom(directSubclassTypeConstructor,baseType.typeSymbol)
directSubclassTypeParam -> baseTypeArgument
}
typeParamsRelationship.toMap
}
一个用法示例:
scala> import scala.reflect.runtime.universe._
scala> typeParametersToBaseTypeArgumentsRelationship(
typeOf[Function1[Long,List[String]]],typeOf[PartialFunction[_,_]].typeConstructor
)
val res1: Map[reflect.runtime.universe.Type,reflect.runtime.universe.Type] =
Map(A -> Long,B -> List[String])
另一个更有趣的用法示例:
sealed trait X[A1,A2,A3]
class Y[B1,B2] extends X[B2,List[B1],B1] {}
scala> typeParametersToBaseTypeArgumentsRelationship(
typeOf[X[Long,List[String],String]],typeOf[Y[_,_]].typeConstructor
)
val res2: Map[reflect.runtime.universe.Type,reflect.runtime.universe.Type] =
Map(B2 -> Long,List[B1] -> List[String],B1 -> String)
缺少该问题的部分是创建directSubclassTypeConstructor
的副本,该副本具有根据给定关系实例化的类型参数。那需要我没有的知识。
编辑: 找到了如何将类型参数应用于类型构造函数后,我就能完成此答案。
import scala.reflect.runtime.universe._
/** Given a type `baseType` and a type constructor of one of its direct subclasses `directSubclassTypeConstructor`,creates a type by applying said type constructor to the type arguments that were used to create the `baseType` as seen from said direct subclass.
* @param baseType a type resulting of the instantiation of a type constructor. For example: {{{typeOf[Option[Int]]}}}
* @param directSubclassTypeConstructor the type constructor we want to instantiate such that it is assignable to `baseType`. For example: {{{typeOf[Some[_]].typeConstructor}}}
* @return the type constructed by applying the type constructor `directSubclassTypeConstructor` to the type arguments of `baseType` as seen from said type constructor. For example: {{{typeOf[Some[Int]]}}}*/
def applySubclassTypeConstructor(baseType: Type,directSubclassTypeConstructor: Type): Type = {
val directSubclassTypeParams = directSubclassTypeConstructor.typeParams
if( directSubclassTypeParams.isEmpty) {
directSubclassTypeConstructor
} else {
val baseTypeConstructor = baseType.typeConstructor;
assert(directSubclassTypeConstructor <:< baseTypeConstructor)
val subclassTypeParamsToBaseTypeArgumentsRelationship=
for {
(baseTypeParam,baseTypeArgument) <- baseTypeConstructor.typeParams zip baseType.typeArgs
} yield {
val directSubclassTypeParam = baseTypeParam.asType.toType.asSeenFrom(directSubclassTypeConstructor,baseType.typeSymbol)
directSubclassTypeParam -> baseTypeArgument
}
val directSubclassTypeArguments =
for (subclassTypeParm <- directSubclassTypeParams) yield {
subclassTypeParamsToBaseTypeArgumentsRelationship.find { r =>
r._1.typeSymbol.name == subclassTypeParm.name
}.get._2
}
appliedType(directSubclassTypeConstructor,directSubclassTypeArguments)
}
}
一个简单的用法示例:
scala> applySubclassTypeConstructor(
typeOf[Option[Int]],typeOf[Some[_]].typeConstructor
)
val res1: reflect.runtime.universe.Type =
Some[Int]
一个有趣的用法示例:
sealed trait X[A1,B1] {}
scala> applySubclassTypeConstructor(
typeOf[X[Long,_]].typeConstructor
)
val res2: reflect.runtime.universe.Type =
Y[String,Long]