用例需要在运行时比较两种类型参数的实际类型。用TypeTag可以很容易地做到这一点,但是在scala-3中它是不可取的。因此,我尝试使用TypeTest,但它要求存在一个类型的实例,而我没有这种实例。因此,我实现了TypeId,这是我自己版本的TypeTag。它的方式更加有限和简单。它只支持类型比较,并且只在TypeId的单个实例存在于每个涉及的类型时才能工作。
import scala.collection.{mutable, immutable}
object TypeId {
private var idSequencer: Int = 0
private val typeIds: mutable.HashMap[Int, TypeId[?]] = mutable.HashMap.empty
private def registerNew[A](tm: TypeId[A]): Int = this.synchronized {
idSequencer += 1
typeIds.put(idSequencer, tm)
idSequencer
}
def knownTypeIds: immutable.HashMap[Int, TypeId[?]] = this.synchronized(immutable.HashMap.from(typeIds))
}
trait TypeId[A] {
val id: Int = registerNew(this)
def equalsTo(that: TypeId[?]): Boolean = this.id == that.id
}如您所见,它要求同步必须是线程安全的,这是不方便的;而且knownTypeIds的值易受争用条件的影响。
我想知道是否可以使用宏在编译时创建唯一的ids。我认为要做到这一点,我需要某种编译时全局变量来实现排序器。scala-3宏支持编译时、全局变量还是实现我的目标的替代方案?
编辑:在问完我的问题几分钟后,我想出了一个非常简单的方法来实现我的目标,方法是将标识符设置为带有类型扩展名称的字符串。这样的名称可以很容易地在宏中生成。但是,我不想知道scala-3宏是否支持编译时全局变量。
下面是实现我的目标的代码,它使用类型的名称而不是顺序整数。
import scala.collection.{immutable}
object TypeId {
@volatile
private var typeIds: immutable.Set[String] = immutable.HashSet.empty
def knownTypeIds: immutable.Set[String] = typeIds
private inline def registerNew[A]: String = {
val id = TypeIdMacros.createId[A]
this.synchronized(typeIds += id)
id
}
}
trait TypeId[A] {
val id: String = registerNew[A]
def equalsTo(that: TypeId[?]): Boolean = this.id == that.id
}import scala.quoted.{Quotes, Type, Expr}
object TypeIdMacros {
inline def createId[A]: String = ${createIdImpl[A]}
def createIdImpl[A](using typeA: Type[A], quotes: Quotes): Expr[String] =
Expr(Type.show[A])
}发布于 2022-11-21 17:01:17
这将是一个非常糟糕的想法,因为增量编译-它很难准确地知道什么将被编译或何时编译。您可以很容易地获得在clean编译上工作的内容,但是在开发期间部分重新编译时会失败,因为并不是所有应该触发排序器的代码都会被触发。
(我不确定它是否可行,可能还有其他原因来避免,但这是避免宏中任何类型的命令式代码的一个普遍原因。)
https://stackoverflow.com/questions/74521882
复制相似问题