Почему наименьшая верхняя граница java.lang.Integer и java.lang.Double выводится как ациклический тип?

#generics #scala #types #type-inference

#дженерики #scala #типы #вывод типа

Вопрос:

Рассмотрим этот код:

 val foo = if(true) 
            new java.lang.Double(4) 
          else
            new java.lang.Integer(4)
  

Выводимый тип для foo равен:

 Number with Comparable[_ >: Double with Integer <: Number with 
  Comparable[_ >: Double with Integer <: Number]]
  

Таким образом, в основном компилятор выполняет цикл по границам и прерывается после третьей рекурсии.

Почему недостаточно следующего?

 Number with Comparable[_ >: Double with Integer <: Number]
  

Ответ №1:

Не ответ, а некоторые подсказки, которые неявно используются в REPL. Компилятор не считает, что типы одинаковы. Выводимый тип более специфичен:

 // some type aliases to make reading easier
type Dx = java.lang.Double
type Ix = java.lang.Integer

// the type the compiler came up with:
type Inferred = Number with Comparable[
  _ >: Dx with Ix <: Number with Comparable[_ >: Dx with Ix <: Number]]

// your type:
type Soc = Number with Comparable[_ >: Dx with Ix <: Number]
  

Проверяя, правильно ли я определил псевдонимы типов:

 val d = new java.lang.Double(4)
val i = new java.lang.Integer(4)
val foo: Soc = if (true) d else i
// foo: Soc = 4.0
val foo: Inferred = if (true) d else i
// foo: Inferred = 4.0
  

Типы не совпадают:

 implicitly[Soc =:= Inferred] // error
  

Ваш тип является супертипом предполагаемого типа:

 implicitly[Inferred <:< Soc] // ok
implicitly[Soc <:< Inferred] // error
  

Итак, согласно компилятору, он придумал более конкретный тип, что было бы правильным решением. Обратите внимание, что вариант использования может быть воссоздан следующим образом:

 class N                     // like java.lang.Number

trait C[T]                  // like Comparable

class I extends N with C[I] // like java.lang.Integer
class D extends N with C[D] // like java.lang.Double

type DI = N with C[_ >: D with I <: N with C[_ >: D with I <: N]]
// DI is like the type inferred

type DI_SOC = N with C[_ >: D with I <: N] // your type

val foo: DI = if (true) new D else new I     // ok
val foo: DI_SOC = if (true) new D else new I // ok

implicitly[DI =:= DI_SOC] // error
implicitly[DI <:< DI_SOC] // DI_SOC super type of DI
implicitly[DI_SOC <:< DI] // error
  

Итак, мне интересно, можем ли мы создать класс, который является DI_SOC , но не a DI , который иллюстрировал бы, что DI и DI_SOC не являются одними и теми же типами, и ваш тип не является наименьшей верхней границей.

Хорошо, после того, как я ненадолго выйду за компьютер, а затем повторю попытку. Вот класс, который является DI_SOC , но не DI :

 class A extends N with C[N]
implicitly[A <:< DI_SOC] // ok
implicitly[A <:< DI]     // error
  

Применяется к исходному варианту использования:

 class Ax extends Number with Comparable[Number] {
  def doubleValue() = 0d
  def floatValue() = 0f
  def intValue() = 0
  def longValue() = 0L
  def compareTo(n: Number) = 0
}

implicitly[Ax <:< Soc]      // ok
implicitly[Ax <:< Inferred] // error
  

Следовательно, типы Soc и Inferred не совпадают и Ax доказывают, что Number with Comparable[_ >: Double with Integer <: Number] это не наименьшая верхняя граница…

Другими словами, между ними есть некоторое пространство Double with Integer <: ? <: Number , но не так много между Double with Integer <: ? <: Number with Comparable[_ >: Double with Integer <: Number]