【问题标题】:How to extract types from tuple type?如何从元组类型中提取类型?
【发布时间】:2021-02-21 09:26:53
【问题描述】:

我有密码

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 以及我的类型列表,但我不知道如何从代码中访问。或者可以采用其他一些方法。

提前致谢

【问题讨论】:

  • getTypeTags 的签名是什么?你想让它返回List[MyTypeTag[Option[_]]吗?那么这与res = MyTypeTag[Option[Int]] :: ... :: Nil 相矛盾。列表的所有元素都具有相同的类型。你想要HList 而不是List
  • @DmytroMitin,但这取决于我传递的元组类型参数,所以这里只是一个例子,返回类型是 List[MyTypeTag[Option[_]],其中占位符可以是任何东西
  • 名单还行,现在不想去乱七八糟的员工了

标签: scala generics reflection scala-reflect


【解决方案1】:

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[_ &lt;: Option[_]]] 替换为List[MyTypeTag[Option[_]]]

【讨论】:

  • 谢谢!明天我将以全新的面貌分析它是如何工作的。只有一件事 - case class MyTypeTag[T]()(implicit val typeTag: TypeTag[T]) - 在我的真实课堂上我实际上正在使用它,所以问题是:我可以确定如果我打印 typeTag 并获得正确的名称作为 Option[Int] 它可以保证它正常工作,如果我得到像 $7 $8 这样的字符串,这意味着即使没有抛出异常,它也不会真正起作用
  • @DmitryReutov 如果不理解“正常工作”的含义,就很难说。您没有详细说明您将如何使用您的 MyTypeTag 机器。当然最好不要依赖原始字符串。
  • 简单来说,我为 doobie 查询创建了一个类型 - 一些 orm 系统。例如,我将 MyTypeTag 传递给函数 def query[T, T1, T2](statement, model: T, type1: MyTypeTag[T1], type2: MyTypeTag[T2]) = statement.query[T, (T1, T2)].to[List]。它以某种神奇的方式工作,尽管 T、T1、T2 在编译时并不直接知道,并且根据代码中的某些条件动态创建。
【解决方案2】:

您无法在运行时区分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,因为它在编译时不存在还没有。

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

【讨论】:

  • “在运行时你无法区分MyTypeTag[Int] 的实例和MyTypeTag[String] 的实例......” 好吧,类型标签正是为了克服类型删除。
  • 您也将typeArgstypeParams 混淆了。
  • 在 scala-scastie 中它工作得非常好,但在 intelliJ 中我得到一个错误unbound wildcard type tt.tpe.typeArgs.map(_ =&gt; MyTypeTag[_]())。这是关于什么的?
  • 您能否提供简单的示例如何从无形的泛型中提取类型,我阅读了一半的文档但没有看到这样的示例
  • @DmitryReutov v.type 是单例类型。这行不通。
猜你喜欢
  • 2020-07-14
  • 2019-03-11
  • 1970-01-01
  • 1970-01-01
  • 2020-01-04
  • 1970-01-01
  • 1970-01-01
  • 2020-08-14
  • 2018-07-20
相关资源
最近更新 更多