英文:
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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论