繁体   English   中英

如何从元组类型中提取类型?

[英]How to extract types from tuple type?

我有一个代码

case class MyTypeTag[T] () 

def getTypeTags[TT <: Product : TypeTag] = {
  val subtypesTags: List[MyTypeTag[Option[_]] = ???  
  sybtypesTags
}

val res = getTypeTags[(Int, String, Boolean)] 
// res = MyTypeTag[Option[Int]] :: MyTypeTag[Option[String]] :: MyTypeTag[Option[Boolean]] :: Nil

所以我想调用函数getTypeTags传递任何元组类型作为类型参数,并获取MyTypeTag实例列表,元组内的每种类型都包含在Option

如果在 intelliJ 中我评估表达式typeof[TT]我会看到带有我的类型列表的属性args ,但我不知道如何从代码访问。 或者可以应用一些其他方式。

提前致谢

在编译时必须知道case class MyTypeTag[T]()类型参数T 但似乎您尝试使用运行时反射来定义它。 这行不通(除非您在运行时定义类: toolbox.define(q"case class MyTypeTag[T]()")但这会很棘手)。

您可以尝试使用编译时反射

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def getTypeTags[TT <: Product]: List[MyTypeTag[_ <: Option[_]]] = 
  macro getTypeTagsImpl[TT]

def getTypeTagsImpl[TT: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._
  weakTypeOf[TT].typeArgs.map(t => q"MyTypeTag[Option[$t]]()")
    .foldRight[Tree](q"Nil")((t, ts) => q"$t :: $ts")
}

用法:

getTypeTags[(Int, String, Boolean)] //List(MyTypeTag(), MyTypeTag(), MyTypeTag())

为了验证代码是否正常工作,我们暂时修改MyTypeTag

import scala.reflect.runtime.universe.TypeTag

case class MyTypeTag[T]()(implicit val typeTag: TypeTag[T])

val res = getTypeTags[(Int, String, Boolean)]
res(0).typeTag // TypeTag[Option[Int]]
res(1).typeTag // TypeTag[Option[String]]
res(2).typeTag // TypeTag[Option[Boolean]]

您还可以使用 Shapeless 代替宏

import shapeless.ops.hlist.{FillWith, Mapped, ToList}
import shapeless.{Generic, HList, Poly0}

case class MyTypeTag[T]()

def getTypeTags[TT <: Product] = new {
  def apply[L <: HList, L1 <: HList]()(implicit
    generic: Generic.Aux[TT, L],
    mapped: Mapped.Aux[L, λ[X => MyTypeTag[Option[X]]], L1],
    fillWith: FillWith[myTypeTagPoly.type, L1],
    toList: ToList[L1, MyTypeTag[_ <: Option[_]]]
  ): List[MyTypeTag[_ <: Option[_]]] = 
    fillWith().toList
}

object myTypeTagPoly extends Poly0 {
  implicit def cse[A]: Case0[MyTypeTag[Option[A]]] = at(MyTypeTag[Option[A]]())
}

getTypeTags[(Int, String, Boolean)]() // List(MyTypeTag(), MyTypeTag(), MyTypeTag())

如果您使MyTypeTag协变( MyTypeTag[+T] ),则List[MyTypeTag[_ <: Option[_]]]可以替换为List[MyTypeTag[Option[_]]]

您无法在运行时区分MyTypeTag[Int]的实例和MyTypeTag[String]的实例(要自己检查,请尝试

val l = List(MyTypeTag[Option[Int]](), MyTypeTag[Option[String]](), MyTypeTag[Option[Boolean]]())

看看它给了你什么,你能用它做什么,不能用它做什么),所以你问的问题的答案是

def getTypeTags[TT <: Product](implicit tt: TypeTag[TT]): List[MyTypeTag[_]] = {
  tt.tpe.typeParams.map(_ => MyTypeTag[Option[_]]())
}

您可以使用tt.tpe.typeParams获取类型参数,但由于这是一个运行时值,您无法将其恢复为MyTypeTag[T]的编译时类型T ,因为它在编译MyTypeTag[T]不存在。

也许你可以利用 shapeless 来做任何你想做的事情,它有办法对元组进行抽象。 https://underscore.io/books/shapeless-guide/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM