Извлечение тега типа с помощью Witness

Я провожу некоторые эксперименты с 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?


person Some Name    schedule 19.12.2019    source источник
comment
Вы компилируете с включенным оптимизатором?   -  person Jasper-M    schedule 19.12.2019
comment
@Jasper-M Я не передал компилятору никаких дополнительных параметров, ..   -  person Some Name    schedule 19.12.2019
comment
Я не могу воспроизвести шаблон байт-кода, который вы вставили. Ни с оптимизатором, ни без него.   -  person Jasper-M    schedule 19.12.2019
comment
@ Jasper-M Не могли бы вы подсказать, какой у вас байт-код?   -  person Some Name    schedule 19.12.2019
comment
@Jasper-M и какой компилятор scala вы используете.   -  person Some Name    schedule 19.12.2019
comment
Я вижу вызовы getTaggedValue, Witness.mkWitness... На Scala 2.13.1. На 2.12.10 я теперь вижу байт-код, похожий на ваш. Очень странный.   -  person Jasper-M    schedule 19.12.2019
comment
@ Jasper-M Хм... Я использую shapeless 2.3.3 и Ligthbend Scala 2.12.9. Какие версии вы используете?   -  person Some Name    schedule 19.12.2019


Ответы (1)


В вызове метода getTaggedValue("xxx" ->> 32) тип результата — одноэлементный тип "xxx". Компилятор Scala 2.12, в принципе, встроит каждое выражение с одноэлементным типом в соответствующее значение (из-за отсутствия лучшей терминологии «значение» типа "xxx" — это строка "xxx"). Таким образом, в этом случае все выражение getTaggedValue("xxx" ->> 32) и все, что оно включает, например построение "xxx" ->> 32 и вызов неявного Witness, будут стерты до простого значения "xxx".

Типы Singleton были немного переработаны для Scala 2.13, чтобы сделать их первоклассными гражданами языка, и в этом процессе встраивание стало менее агрессивным. Потому что, если вы добавите побочные эффекты к вашему методу getTaggedValue, вы увидите, что это встраивание не всегда безопасно. Это означает, что при компиляции со Scala 2.13 вы увидите такие вызовы, как getTaggedValue и Witness.mkWitness в байт-коде.

person Jasper-M    schedule 19.12.2019