How to create a TypeTag manually?

前端 未结 4 1560
悲&欢浪女
悲&欢浪女 2020-11-28 13:43

I\'m interested in creating a TypeTag manually (since 2.10M5):

object X {
  import reflect.runtime.universe._
  def tt[A : TypeTag](a: A) = typeTag[A] // how         


        
4条回答
  •  囚心锁ツ
    2020-11-28 14:38

    Currently, there are three ways to create a TypeTag that supports generics manually. The first two depends on the scala-compiler, and with them you get type checking just like in compile time, as it is an actual code compilation:

    import scala.reflect.runtime.universe._
    import scala.reflect.runtime.currentMirror
    import scala.tools.reflect.ToolBox
    
    val toolbox = currentMirror.mkToolBox()
    
    def createTypeTag(tp: String): TypeTag[_] = {
      val ttree = toolbox.parse(s"scala.reflect.runtime.universe.typeTag[$tp]")
      toolbox.eval(ttree).asInstanceOf[TypeTag[_]]
    }
    

    If you need a serializable TypeTag and performance isn't your main concern, the first method is the most succinct. OTOH, if your use case needs to be very performant, beware that on-the-fly compilation might take a couple of seconds to finish.

    The second method performs a little better:

    def createTypeTag(tp: String): TypeTag[_] = {
      val ttagCall = s"scala.reflect.runtime.universe.typeTag[$tp]"
      val tpe = toolbox.typecheck(toolbox.parse(ttagCall), toolbox.TYPEmode).tpe.resultType.typeArgs.head
    
      TypeTag(currentMirror, new reflect.api.TypeCreator {
        def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
          assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
          tpe.asInstanceOf[U#Type]
        }
      }
    }
    

    This second mode creates a typed tree and fetches the concrete types marked in the TypeTag, i.e., if your target is a List[String], it will be translated to scala.collection.immutable.List[String], since scala.List is only a type alias. That's actually the behavior expected from typeTag[List[String]].

    The last method is to create a Type manually and use it to create the TypeTag. This method is error-prone, there is limited type checking, and it uses internal API. This is, however, the fastest way to manually obtain a TypeTag.

    def createTypeTag(tp: String): TypeTag[_] = {
      val typs = // ... manipulate the string to extract type and parameters
      val typSym = currentMirror.staticClass(typs[0])
      val paramSym = currentMirror.staticClass(typs[1])
    
      val tpe = universe.internal.typeRef(NoPrefix, typSym, List(paramSym.selfType))
    
      val ttag = TypeTag(currentMirror, new TypeCreator {
        override def apply[U <: Universe with Singleton](m: Mirror[U]): U#Type = {
          assert(m == currentMirror, s"TypeTag[$tpe] defined in $currentMirror cannot be migrated to $m.")
          tpe.asInstanceOf[U#Type]
        }
      })
    }
    

提交回复
热议问题