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

huangapple go评论56阅读模式
英文:

In Scala 3, is it possible to use declared type of an object in runtime?

问题

在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]] // 在Scala 2中有效

summon[Type[Int]] // 在Scala 3中不起作用:未找到参数x$1的类型quoted.Quotes的给定实例...

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

英文:

In Scala 2, most generic type information of an object is erased at runtime. At this moment, all 3 binary execution environments (JVM, javascript, and LLVM) abide this behaviour, they only differs in minor details in metadata formats.

In a rare case if it incurs critical data loss, or if it triggers a rare binary error. A mechanism should be used to preserve declared type information in an adjoint data structure. The following code gave a short example of such data structure in 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)
}

Any variable declared as type Unerase[T] instead of T will be guaranteed to have its full declared type visible at runtime. Unfortunately, this example no longer works in 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 ...

Is there a mechanism that I can use to implement the same mechanism to fully mitigate type erasure?

答案1

得分: 1

以下是您提供的代码的翻译部分:

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) 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(b

<details>
<summary>英文:</summary>

Were you looking for something like the following (using my approach in https://stackoverflow.com/questions/75752812/is-there-a-simple-scala-3-example-of-how-to-use-quoted-type-as-replacement-for)?

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])


</details>

huangapple
  • 本文由 发表于 2023年2月16日 07:26:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75466372.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定