За любым типом может следовать {}
прилагаемая последовательность типов и абстрактных нечетких определений элементов. Это известно как «уточнение» и используется для обеспечения дополнительной точности по базовому типу, который уточняется. На практике уточнения наиболее часто используются для выражения ограничений на элементы абстрактного типа уточняемого типа.
Это малоизвестный факт, что этой последовательности разрешено быть пустым и в форме, которую вы можете видеть в бесформенном исходном коде, T {}
- это тип T
с пустой утонченностью. Любое пустое уточнение ... пустое ... поэтому не добавляет никаких дополнительных ограничений к уточненному типу, и поэтому типы T
и T {}
эквивалентны. Мы можем получить компилятор Scala, чтобы убедиться, что для нас, как это так,
scala> implicitly[Int =:= Int {}]
res0: =:=[Int,Int] = <function1>
Так почему бы мне сделать такое, по-видимому бессмысленные вещи в бесформенную? Это связано с взаимодействием между наличием уточнений и выводами типа. Если вы посмотрите в the relevant section Спецификации языка Scala, вы увидите, что алгоритм выбора типа пытается избежать вывода одиночных типов по крайней мере в некоторых случаях. Вот пример этого делать как раз то,
scala> class Foo ; val foo = new Foo
defined class Foo
foo: Foo = [email protected]
scala> val f1 = foo
f1: Foo = [email protected]
scala> val f2: foo.type = foo
f2: foo.type = [email protected]
Как видно из определения f2
компилятор Scala знает, что значение foo
имеет более точный тип foo.type
(то есть. Типа синглтон из val foo
), однако, если явно не запрошено, это не сделает вывод о более точном типе. Вместо этого он отображает неэлементный (то есть расширенный) тип Foo
, как вы можете видеть в случае f1
.
Но в случае Witness
в бесформенном я явно хочу одноточечный типа, чтобы сделать вывод для использования в value
элемента (вся точка Witness
это дает нам возможность проходить между уровнями типа и значением с помощью одноэлементных типов) , так можно ли убедить компилятора Scala в этом?
Оказывается, что пустая утонченность делает именно это,
scala> def narrow[T <: AnyRef](t: T): t.type = t
narrow: [T <: AnyRef](t: T)t.type
scala> val s1 = narrow("foo") // Widened
s1: String = foo
scala> def narrow[T <: AnyRef](t: T): t.type {} = t // Note empty refinement
narrow: [T <: AnyRef](t: T)t.type
scala> val s2 = narrow("foo") // Not widened
s2: String("foo") = foo
Как вы можете видеть в приведенном выше REPL транскрипте, в первом случае s1
был введен в качестве расширенного типа String
тогда s2
был назначен одноэлементный тип String("foo")
.
Является ли это санкционированным SLS? Нет, но это согласуется с ним, и это имеет какой-то смысл. Большая часть механики вывода типа Scala - это реализация, а не spec'ed, и это, вероятно, один из наименее неожиданных и проблемных примеров.
Что означает «расширение» в сообщении фиксации: «Очевидно, что пустое средство уточнения« не расширяет меня ».? –
Расширение относится к неявному расширению типа (например, List (1, 2.0) является List [Double], потому что int 1 можно расширить до double, чтобы он соответствовал 2.0). Этот смысл может помочь объяснить это: https://gist.github.com/milessabin/65fa0d4ef373781d3ab4 –
Widen означает расширенный тип объекта X из одноэлементного типа 'X.type', поскольку обычно вы хотите избежать вывода одиночных типов, но здесь нужен одноэлементный тип. –