2016-03-09 5 views
38

просмотр Shapeless код, я наткнулся на это, казалось бы, посторонняя {}here и here:Что `T {}` делать в Scala

trait Witness extends Serializable { 
    type T 
    val value: T {} 
} 

trait SingletonOps { 
    import record._ 
    type T 
    def narrow: T {} = witness.value 
} 

я почти проигнорировал это опечатка, так как она ничего не делает, но, видимо, это делает что нибудь. См. Этот фиксаж: https://github.com/milessabin/shapeless/commit/56a3de48094e691d56a937ccf461d808de391961

Я понятия не имею, что он делает. Может кто-нибудь объяснить?

+1

Что означает «расширение» в сообщении фиксации: «Очевидно, что пустое средство уточнения« не расширяет меня ».? –

+0

Расширение относится к неявному расширению типа (например, List (1, 2.0) является List [Double], потому что int 1 можно расширить до double, чтобы он соответствовал 2.0). Этот смысл может помочь объяснить это: https://gist.github.com/milessabin/65fa0d4ef373781d3ab4 –

+0

Widen означает расширенный тип объекта X из одноэлементного типа 'X.type', поскольку обычно вы хотите избежать вывода одиночных типов, но здесь нужен одноэлементный тип. –

ответ

51

За любым типом может следовать {} прилагаемая последовательность типов и абстрактных нечетких определений элементов. Это известно как «уточнение» и используется для обеспечения дополнительной точности по базовому типу, который уточняется. На практике уточнения наиболее часто используются для выражения ограничений на элементы абстрактного типа уточняемого типа.

Это малоизвестный факт, что этой последовательности разрешено быть пустым и в форме, которую вы можете видеть в бесформенном исходном коде, 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, и это, вероятно, один из наименее неожиданных и проблемных примеров.

+0

Во-первых, эквивалентные типы должны вести себя одинаково и должны иметь одинаковое представление, поэтому я находят это удивительным и злым. Но я смущен поведением: я предполагаю, что расширение применяется к '" foo ".type {}' и не расширяет его, не так ли? Можно ли скрыть 'T {}' за макрокомандой 'T @ narrow'? – Blaisorblade

+0

Да, это так. Я действительно не вижу причин думать, что макрокоманда превосходит использование существующего, но в остальном неиспользуемого синтаксиса. –

+0

Спасибо за отличное объяснение @MilesSabin – pathikrit

Смежные вопросы