在Scala 3中,是否可以在运行时使用对象的声明类型?

d4so4syb  于 2023-03-18  发布在  Scala
关注(0)|答案(1)|浏览(191)

在Scala 2中,对象的大部分泛型类型信息在运行时都会被擦除。目前,所有三个二进制执行环境(JVM、javascript和LLVM)都遵守这一行为,它们只是在元数据格式的细节上有所不同。
在极少数情况下,如果它导致关键数据丢失,或者触发了罕见的二进制错误。应该使用一种机制来保留伴随数据结构中声明的类型信息。下面的代码给出了Scala 2中此类数据结构的一个简短示例:

import scala.reflect.runtime.universe
import scala.collection.concurrent.TrieMap
import scala.language.implicitConversions

case class Unerase[T](self: T)(
    implicit
    ev: universe.TypeTag[T]
) {

  import Unerase._

  cache += {

    val inMemoryId = System.identityHashCode(this)
    inMemoryId -> ev
  }
}

object Unerase {

  lazy val cache = TrieMap.empty[Int, universe.TypeTag[_]]

  def get[T](v: T): Option[universe.TypeTag[T]] = {
    val inMemoryId = System.identityHashCode(v)
    cache.get(inMemoryId).map { tt =>
      tt.asInstanceOf[universe.TypeTag[T]]
    }
  }

  implicit def unbox[T](v: Unerase[T]): T = v.self

  implicit def box[T](v: T)(
      implicit
      ev: universe.TypeTag[T]
  ): Unerase[T] = Unerase(v)
}

任何声明为Unerase[T]而不是T类型的变量都可以保证在运行时显示其完整声明的类型。不幸的是,这个例子在Scala 3中不再有效:

implicitly[TypeTag[Int]] // works in Scala 2

summon[Type[Int]] // doesn't work in Scala 3: No given instance of type quoted.Quotes was found for parameter x$1 ...

是否有一种机制可以用来实现完全减轻类型擦除的相同机制?

qco9c6ql

qco9c6ql1#

您是否在寻找类似下面的内容(使用我在Is there a simple Scala 3 example of how to use quoted.Type as replacement for TypeTag?中使用的方法)?

import scala.collection.concurrent.TrieMap

case class Unerase[T: TypeTag](self: T):
  import Unerase._
  cache += {
    val inMemoryId = System.identityHashCode(this)
    inMemoryId -> typeTag[T]
  }

object Unerase:
  lazy val cache = TrieMap.empty[Int, TypeTag[_]]

  def get[T](v: T): Option[TypeTag[T]] =
    val inMemoryId = System.identityHashCode(v)
    cache.get(inMemoryId).map { tt =>
      tt.asInstanceOf[TypeTag[T]]
    }

  given [T]: Conversion[Unerase[T], T] = _.self
  given [T: TypeTag]: Conversion[T, Unerase[T]] = Unerase(_)
case class TypeTag[T](tpe: my.Type)
object TypeTag:
  inline given [T]: TypeTag[T] = TypeTag(getType[T])

def typeTag[T: TypeTag]: TypeTag[T] = summon[TypeTag[T]]
def typeOf[T: TypeTag]: my.Type = summon[TypeTag[T]].tpe

object my:
  sealed trait Type
  case class ConstantType(constant: Constant) extends Type
  sealed trait NamedType extends Type:
    def qualifier: Type
    def name: String
  case class TermRef(qualifier: Type, name: String) extends NamedType
  case class TypeRef(qualifier: Type, name: String) extends NamedType //
  case class SuperType(thisTpe: Type, superTpe: Type) extends Type
  case class Refinement(parent: Type, name: String, info: Type) extends Type
  case class AppliedType(tycon: Type, args: List[Type]) extends Type
  case class AnnotatedType(underlying: Type, annot: Term) extends Type
  sealed trait AndOrType extends Type:
    def left: Type
    def right: Type
  case class AndType(left: Type, right: Type) extends AndOrType
  case class OrType(left: Type, right: Type) extends AndOrType
  case class MatchType(bound: Type, scrutinee: Type, cases: List[Type]) extends Type
  case class ByNameType(underlying: Type) extends Type
  case class ParamRef(binder: Type, paramNum: Int) extends Type //
  case class ThisType(tref: Type) extends Type //
  case class RecursiveThis(binder: RecursiveType) extends Type //
  case class RecursiveType(underlying: Type, recThis: RecursiveThis) extends Type //
  sealed trait LambdaType extends Type:
    def paramNames: List[String]
    def paramTypes: List[Type]
    def resType: Type
  sealed trait MethodOrPoly extends LambdaType
  case class MethodType(paramNames: List[String], paramTypes: List[Type], resType: Type) extends MethodOrPoly
  case class PolyType(paramNames: List[String], paramTypes: List[TypeBounds], resType: Type) extends MethodOrPoly
  case class TypeLambda(paramNames: List[String], paramTypes: List[TypeBounds], resType: Type) extends LambdaType
  case class MatchCase(pattern: Type, rhs: Type) extends Type
  case class TypeBounds(low: Type, hi: Type) extends Type
  case object NoPrefix extends Type

  sealed trait Term
  case class New(tpe: Type/*tpt: TypeTree*/) extends Term

  sealed trait Constant
  case class BooleanConstant(b: Boolean) extends Constant
  case class ByteConstant(b: Byte) extends Constant
  case class ShortConstant(s: Short) extends Constant
  case class IntConstant(i: Int) extends Constant
  case class LongConstant(l: Long) extends Constant
  case class FloatConstant(f: Float) extends Constant
  case class DoubleConstant(d: Double) extends Constant
  case class CharConstant(c: Char) extends Constant
  case class StringConstant(s: String) extends Constant
  case object UnitConstant extends Constant
  case object NullConstant extends Constant
  case class ClassOfConstant(tpe: Type) extends Constant

import scala.quoted.*

inline def getType[T]: my.Type = ${getTypeImpl[T]}

def getTypeImpl[T: Type](using Quotes): Expr[my.Type] =
  import quotes.reflect.*

  def mkConstant(constant: Constant): Expr[my.Constant] = constant match
    case BooleanConstant(b) => '{my.BooleanConstant(${ Expr(b) })}
    case ByteConstant(b) => '{my.ByteConstant(${ Expr(b) })}
    case ShortConstant(s) => '{my.ShortConstant(${ Expr(s) })}
    case IntConstant(i) => '{my.IntConstant(${ Expr(i) })}
    case LongConstant(l) => '{my.LongConstant(${ Expr(l) })}
    case FloatConstant(f) => '{my.FloatConstant(${ Expr(f) })}
    case DoubleConstant(d) => '{my.DoubleConstant(${ Expr(d) })}
    case CharConstant(c) => '{my.CharConstant(${ Expr(c) })}
    case StringConstant(s) => '{my.StringConstant(${ Expr(s) })}
    case UnitConstant() => '{my.UnitConstant}
    case NullConstant() => '{my.NullConstant}
    case ClassOfConstant(tpe) => '{my.ClassOfConstant(${ mkType(tpe) })}

  def mkType(tpe: TypeRepr): Expr[my.Type] = tpe match
    case ConstantType(constant) => '{ my.ConstantType(${mkConstant(constant)}) }
    case TermRef(qualifier, name) => '{my.TermRef(${mkType(qualifier)}, ${Expr(name)})}
    case TypeRef(qualifier, name) => '{my.TypeRef(${mkType(qualifier)}, ${Expr(name)})}
    case SuperType(thisTpe, superTpe) => '{my.SuperType(${mkType(thisTpe)}, ${mkType(superTpe)})}
    case Refinement(parent, name, info) => '{my.Refinement(${mkType(parent)}, ${Expr(name)}, ${mkType(info)})}
    case AppliedType(tycon, args) => '{my.AppliedType(${mkType(tycon)}, ${Expr.ofList(args.map(mkType))})}
    case AnnotatedType(underlying, annot) => '{my.AnnotatedType(${mkType(underlying)}, ${mkTerm(annot)})}
    case AndType(left, right) => '{my.AndType(${mkType(left)}, ${mkType(right)})}
    case OrType(left, right) => '{my.OrType(${mkType(left)}, ${mkType(right)})}
    case MatchType(bound, scrutinee, cases) => '{my.MatchType(${mkType(bound)}, ${mkType(scrutinee)}, ${Expr.ofList(cases.map(mkType))})}
    case ByNameType(underlying) => '{my.ByNameType(${mkType(underlying)})}
    case ParamRef(binder, paramNum) => '{my.ParamRef(${mkType(binder)}, ${Expr(paramNum)})}
    case ThisType(tref) => '{my.ThisType(${mkType(tref)})}
    case RecursiveThis(binder) => '{my.RecursiveThis(${mkRecursiveType(binder)})}
    case MethodType(paramNames, paramTypes, resType) => '{my.MethodType(${Expr(paramNames)}, ${Expr.ofList(paramTypes.map(mkType))}, ${mkType(resType)})}
    case PolyType(paramNames, paramTypes, resType) => '{my.PolyType(${Expr(paramNames)}, ${Expr.ofList(paramTypes.map(mkTypeBounds))}, ${mkType(resType)})}
    case TypeLambda(paramNames, paramTypes, resType) => '{my.TypeLambda(${Expr(paramNames)}, ${Expr.ofList(paramTypes.map(mkTypeBounds))}, ${mkType(resType)})}
    case MatchCase(pattern, rhs) => '{my.MatchCase(${mkType(pattern)}, ${mkType(rhs)})}
    case TypeBounds(low, hi) => '{my.TypeBounds(${mkType(low)}, ${mkType(hi)})}
    case NoPrefix() => '{my.NoPrefix}

  def mkTerm(term: Term): Expr[my.Term] = term match
    case New(tpt) => '{my.New(${mkType(tpt.tpe)})}

  def mkRecursiveThis(recThis: RecursiveThis): Expr[my.RecursiveThis] = recThis match
    case RecursiveThis(binder) => '{my.RecursiveThis(${mkRecursiveType(binder)})}
  def mkRecursiveType(recTpe: RecursiveType): Expr[my.RecursiveType] =
    '{my.RecursiveType(${mkType(recTpe.underlying)}, ${mkRecursiveThis(recTpe.recThis)})}
  def mkTypeBounds(typeBounds: TypeBounds): Expr[my.TypeBounds] = typeBounds match
    case TypeBounds(lo, hi) => '{my.TypeBounds(${mkType(lo)}, ${mkType(hi)})}

  mkType(TypeRepr.of[T])

相关问题