Suppose we have a generic type (for example, Seq[E]
) and a concrete subtype (for example, Seq[Int]
). How can we extract concrete type that corresponds to the type parameters of the abstraction type. In other words, how can we know E -> Int
.
Below is a minimal code example that tests for the desired behavior. The extractTypeBinding
function would perform the transformation in question.
import scala.reflect.runtime.{universe => ru}
class MyFuncs
object MyFuncs {
def fn1[E](s: Seq[E]): E = ???
def fn2[K, V](m: Map[K, V]): Int = ???
}
case class Bar[T]()
trait Foo[T] { def bar: Bar[T] }
object Scratch {
def extractTypeBinding(genType: ru.Type, typeParam: ru.Type)(concreteType: ru.Type): ru.Type = ???
def getArgTypes(methodSymbol: ru.MethodSymbol): Seq[ru.Type] =
methodSymbol.paramLists.headOption.getOrElse(Nil).map(_.typeSignature)
def main(a: Array[String]): Unit = {
// Grab the argument types of our methods.
val funcsType = ru.typeOf[MyFuncs].companion
val fn1ArgTypes = getArgTypes(funcsType.member(ru.TermName("fn1")).asMethod)
val fn2ArgTypes = getArgTypes(funcsType.member(ru.TermName("fn2")).asMethod)
val genericSeq = fn1ArgTypes.head // Seq[E]
val genericMap = fn2ArgTypes.head // Map[K, V]
// Create an extractor for the `E` in `Seq[E]`.
val seqElExtractor = extractTypeBinding(genericSeq, genericSeq.typeArgs.head) _
// Extractor for the `K` in `Map[K,V]`
val mapKeyExtractor = extractTypeBinding(genericMap, genericMap.typeArgs.head) _
// Extractor for the `V` in `Map[K,V]`
val mapValueExtractor = extractTypeBinding(genericMap, genericMap.typeArgs(1)) _
println(seqElExtractor(ru.typeOf[Seq[Int]])) // should be Int
println(seqElExtractor(ru.typeOf[Seq[Map[String, Double]]])) // should be Map[String, Double]
println(mapKeyExtractor(ru.typeOf[Map[String, Double]])) // should be String
println(mapKeyExtractor(ru.typeOf[Map[Int, Boolean]])) // should be Int
println(mapValueExtractor(ru.typeOf[Map[String, Double]])) // should be Double
println(mapValueExtractor(ru.typeOf[Map[Int, Boolean]])) // should be Boolean
}
}
Based on the docstrings, it seems like asSeenFrom
should be the key to implementing extractTypeBinding
. I tried the below implementation, but it returned the type parameter unchanged.
def extractTypeBinding(genType: ru.Type, typeParam: ru.Type)(concreteType: ru.Type): ru.Type =
typeParam.asSeenFrom(concreteType, genType.typeSymbol.asClass)
...
println(seqElExtractor(ru.typeOf[Seq[Int]])) // E
println(seqElExtractor(ru.typeOf[Seq[Map[String, Double]]])) // E
If asSeenFrom
is the correct approach, what would the correct incantation be? If not, then how should this be done?
Aucun commentaire:
Enregistrer un commentaire