mardi 2 février 2021

Create Seq[T] reflectively for a T known runtime

I have a type T known reflectively runtime as a value of scala.reflect.runtime.Type. I would like to create a type value representing scala.collection.Seq[T]. How can I do this?

A background what I want this for:

I want to be able to extract reflectively generic types contained in companion objects of various types. The types have a form of type As[T] = T or type As[T] = Seq[T]. I want to be able to get a concrete type in a form of X or Seq[X] for any X. The first case is already working for me, as in that case I simply return a Type of X I already have. The second case is harder - I have a Type for Seq[T] which is generic and a Type for a concrete X, but I do not know how could I either modify the Seq[T] I already have or create a brand new Seq[X].

import scala.reflect.runtime.universe._

object Wrap {
  type As[T] = T
}
case class Wrap[T](value: T)

object WrapSeq {
  type As[T] = Seq[T]
}
case class WrapSeq[T](value: Seq[T])

object Main extends App {

  def checkType(typeSignature: Type): Type = {
    // find mapping in the companion object
    val companion = typeSignature.typeSymbol.companion
    val member = companion.typeSignature.member(TypeName("As"))

    if (member != NoSymbol && member.isType) {
      val memberType = member.asType

      def extractSingleType(types: Seq[Type]): Option[Type] = types match {
        case Seq(head) => Some(head)
        case _ => None
      }
      (memberType.typeParams, typeSignature.typeArgs) match {
        case (Seq(typePar), Seq(typeArg)) if memberType.isAliasType =>
          if (member.typeSignature.resultType.typeSymbol.fullName == typePar.fullName) {
            // As[T] == T
            typeArg
          } else if (member.typeSignature.resultType.typeSymbol.fullName == "scala.collection.Seq" && extractSingleType(member.typeSignature.resultType.typeArgs).exists(_.typeSymbol.name == typePar.name)) {
            // As[T] == Seq[T]
            // we want to return Seq[typeArg] here
            // we need to construct the type reflectively somehow
            ???
          } else {
            throw new UnsupportedOperationException(s"Unsupported As value $typePar for $typeSignature")
          }
        case _ => // no type parameter, handle the plain type
          member.typeSignature
      }
    } else {
      typeSignature
    }
  }

  println(checkType(typeOf[Wrap[String]])) // output: String

  println(checkType(typeOf[WrapSeq[String]])) // desired output: Seq[String]

}




Aucun commentaire:

Enregistrer un commentaire