Типы, зависящие от пути (для типов и для внутренних классов) - PullRequest
0 голосов
/ 11 января 2019

Экспериментируя с типами, зависящими от пути, я получил неожиданные результаты:

object Funny1 {
  class X {
    type Y = String
    val y: Y = "y"
  }

  val x1 = new X
  val x2 = new X

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = () // 1.3
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1.y)
  foo(x1)(x2.y) // <-- 1.1 would expect this to fail

  foo_diff(x1.y)(x2.y)
  foo_diff(x2.y)(x2.y) // <-- 1.2 would expect this to fail

  foo_gen(x1.y)(x2.y)
  foo_gen(x2.y)(x2.y)
}


object Funny2 {
  class X {
    class Y {
    }
  }

  val x1 = new X
  val x2 = new X

  val x1y = new x1.Y
  val x2y = new x2.Y

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1y)
  // foo(x1)(x2y) // does not compile

  foo_diff(x1y)(x2y)
  // foo_diff(x2y)(x2y) // does not compile

  foo_gen(x1y)(x2y)
  foo_gen(x2y)(x2y)
}

object Funny3 {
  trait X {
    type Y
    def y: Y
  }

  val x1 = new X {
    override type Y = String

    override def y: String = "y"
  }
  val x2 = new X {
    override type Y = Int

    override def y: Int = 3
  }

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1.y)
  // foo(x1)(x2.y)    // 3.1 fails as expected

  foo_diff(x1.y)(x2.y)
  // foo_diff(x2.y)(x2.y)  // 3.2 fails as expected

  foo_gen(x1.y)(x2.y)
  foo_gen(x2.y)(x2.y)
}



object Funny3b {
  trait X {
    type Y
    def y: Y
  }

  val x1 = new X {
    override type Y = String

    override def y: String = "y"
  }
  val x2 = new X {
    override type Y = String

    override def y: String = "y2"
  }

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1.y)
  foo(x1)(x2.y)    // 3b.1 does not fail

  foo_diff(x1.y)(x2.y)
  foo_diff(x2.y)(x2.y)  // 3b.2 does not fail

  foo_gen(x1.y)(x2.y)
  foo_gen(x2.y)(x2.y)
}

Особенно меня интересуют ответы на вопросы:

  • Почему компилируется строка, помеченная как 1.1 и 1.2)?
  • Почему нет ссылки на x в качестве первого параметра, необходимого в 1,3 foo_diff?
  • Почему строки 3.1 и 3.2 не компилируются, а 3b.1 и 3b.2 компилируются? Тем более, что зависимость пути, по-видимому, «потеряна» в 3b, если базовые типы можно определить как идентичные (здесь: String).

Спасибо, Мартин

1 Ответ

0 голосов
/ 13 января 2019

Я думаю, что фундаментальное недоразумение, лежащее в основе всего вашего вопроса, заключается в том, что вы предполагаете, что конструкция:

trait / class ClassName {
  type T = something
} 

создает зависимый тип. Это не так. Если вы откроете подраздел 3.5.1 Эквивалентность раздела 3.5 «Отношения между типами» спецификации, вы можете увидеть, что:

  • Если t определяется псевдонимом типа t = T, то t эквивалентно T.

Вот почему в вашем первом примере, например, компилятор видит x1.Y и x2.Y и X#Y как просто String. Если вы замените все это просто String, то не останется вопросов о том, почему этот код компилируется.

Точно так же в вашем третьем примере просто определите некоторые псевдонимы. Если вы замените эти псевдонимы их определениями, то снова станет ясно, почему этот код компилируется и дает сбой.

Ваш второй пример использует другую конструкцию

trait / class ClassName {
  trait / class InnerName
} 

Это конструкция, которая создает зависимый от пути тип, и, следовательно, код, который вы не собираетесь компилировать, на самом деле дает сбой. Насколько я понимаю, это единственный из ваших примеров, в котором действительно используются зависимые типы.

Также, если вы откроете статьи Tour of Scala Абстрактные члены типа и Внутренние классы , которые описывают первую и вторую конструкции, вы можете заметить, что только последняя (но не первая) !) упоминает "зависимые от пути типы".

...