我正在用Witness
进行一些实验,现在试图了解它是如何工作的。请考虑以下示例:
import shapeless.syntax.singleton._
import shapeless.labelled.FieldType
import shapeless.Witness
def main(args : Array[String]): Unit = {
println(getTaggedValue("xxx" ->> 32))
println(getTaggedValue("yyy" ->> 44))
}
def getTaggedValue[TypeTag, Value](kt: FieldType[TypeTag, Value])
(implicit witness: Witness.Aux[TypeTag]): TypeTag = witness.value
正如预期的那样,程序打印
xxx
yyy
我试图了解这些xxx
值和yyy
值是如何获得的,并打印了main
函数的实际字节码:
62: getstatic #69 // Field scala/Predef$.MODULE$:Lscala/Predef$;
65: ldc #71 // String xxx
67: invokevirtual #75 // Method scala/Predef$.println:(Ljava/lang/Object;)V
70: getstatic #69 // Field scala/Predef$.MODULE$:Lscala/Predef$;
73: ldc #77 // String yyy
75: invokevirtual #75 // Method scala/Predef$.println:(Ljava/lang/Object;)V
可以看出,所有这些Witness
技巧都没有出现。隐含的Witness.Aux[TypeTag]
来自implicit def apply[T]: Witness.Aux[T] = macro SingletonTypeMacros.materializeImpl[T]
它实际上是如何工作的?是编译器优化了这些方法调用吗?或者这是通过macro
完成的?
在方法调用getTaggedValue("xxx" ->> 32)
中,结果类型是单例类型"xxx"
。Scala 2.12 编译器原则上会将每个具有单例类型的表达式内联到随附的值(由于缺乏更好的术语,类型"xxx"
的"值"是字符串"xxx"
(。所以在这种情况下,整个表达式getTaggedValue("xxx" ->> 32)
及其包含的所有内容,如构造"xxx" ->> 32
和调用隐式Witness
,都将被擦除为简单值"xxx"
。
Scala 2.13 对单例类型进行了一些修改,使它们成为该语言中的一等公民,并且在此过程中内联变得不那么激进。因为如果您将副作用添加到getTaggedValue
方法中,您会发现此内联并不总是安全的。这意味着当你使用 Scala 2.13 编译时,你会在字节码中看到getTaggedValue
和Witness.mkWitness
等调用。