вывести общий супертип на основе значения параметра и типов параметров функции - PullRequest
7 голосов
/ 28 июня 2010

Должно ли следующее компилироваться без необходимости явного определения типа в this?

def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] =
  prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))

Мне кажется, что тип должен быть в состоянии вывести. Это просто ограничение в компиляторе Scala, или есть теоретико-типовая причина, по которой это невозможно? У меня пока нет ощущения того, что можно ожидать от логического вывода типа Scala.

Работа через этот метод:

  • B >: A по определению
  • this имеет тип PlayList[A], который является подтипом PlayList[B], поскольку B >: A и PlayList ковариантны в A.
  • node имеет тип B, тип параметра prefix.
  • второй параметр к параметру функции f в foldr имеет тот же тип (объявлен B), что и первый параметр для foldr.
  • Таким образом, suffix имеет тот же тип, что и this, поэтому, в частности, это PlayList[A]. Так как B >: A, suffix.prepNode() занимает B.

Я бы хотел, чтобы компилятор увидел, что suffix.prepNode(node) допустимо, когда node имеет тип B. Похоже, что это возможно сделать, только если я явно укажу тип в вызове foldr или в ссылке на this в этом вызове.

Интересно, что если я указываю явные типы в параметрах функции как (node: B, suffix: PlayList[B]), ошибка параметра несоответствия типов все еще генерируется в параметре для вызова метода suffix.prepNode(node): "found: B, required: A"

Я использую Scala 2.8 RC6. Полный пример ниже, рассматриваемая строка - строка 8.

sealed abstract class PlayList[+A] {
  import PlayList._
  def foldr[B](b: B)(f: (A, B) => B): B

  def prepNode[B >: A](b: B): PlayList[B] = nel(b, this)
  def prepList[B >: A](prefix: PlayList[B]): PlayList[B] =
    // need to specify type here explicitly
    prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))

  override def toString = foldr("")((node, string) => node + "::" + string)
}

object PlayList {
  def nil[A]: PlayList[A] = Nil
  def nel[A](head: A, tail: PlayList[A]): PlayList[A] = Nel(head, tail)
  def nel[A](as: A*): PlayList[A] = as.foldRight(nil[A])((a, l) => l.prepNode(a))
}

case object Nil extends PlayList[Nothing] {
  def foldr[B](b: B)(f: (Nothing, B) => B) = b
}
case class Nel[+A](head: A, tail: PlayList[A]) extends PlayList[A] {
  def foldr[B](b: B)(f: (A, B) => B) = f(head, tail.foldr(b)(f))
}

РЕДАКТИРОВАТЬ: вторая попытка рассуждать через этапы компиляции

  • Переименование для наглядности, foldr принимает параметры типов (T)((U, T) => T). Мы пытаемся вывести значения типов U и T.
  • Существует связь между первым параметром foldr и вторым параметром функции - это одно и то же, T. (В частичном ответе Даниилу.)
  • Типы объектов, которые мы передаем в качестве этих параметров: this: PlayList[A] и suffix: PlayList[B]
  • Итак, начиная с B >: A, наиболее специфический общий супер тип - PlayList[B]; следовательно, у нас есть T == PlayList[B]. Обратите внимание , что нам не нужны никакие отношения между U и T, чтобы сделать это.

Вот где я застрял:

  • Из сообщения об ошибке компиляции выводчик ясно считает, что node имеет тип B (то есть U == B).
  • Я не вижу, как можно прийти к выводу, что U == B, не выводя его из параметра типа suffix. (Может ли компилятор Scala сделать это?)
  • Если на этом этапе логического вывода происходит то, что следует, U == B, и мы успешно скомпилировали. Так какой шаг пошёл не так?

РЕДАКТИРОВАТЬ 2: При переименовании типов параметров foldr выше я пропустил, что U == A по определению, это параметр типа класса PlayList. Я думаю, что это все еще согласуется с вышеупомянутыми шагами, хотя, так как мы вызываем его для экземпляра PlayList[B].

Таким образом, на сайте вызовов T == PlayList[B] является наименее распространенным супертипом пары вещей, а U == B по определению foldr на приемнике. Это кажется достаточно кратким, чтобы ограничиться парой вариантов:

  • компилятор не может разрешить эти несколько типов и вычисляет верхнюю границу B
  • ошибка при получении типа возврата PlayList[B] из foldr к типу параметра prepNode (скептически)

Ответы [ 2 ]

3 голосов
/ 28 июня 2010

Я не эксперт по типам, но вот что происходит, когда я пытаюсь сделать вывод.

((node, suffix) => suffix.prepNode(node)) возвращает неизвестный тип PlayList[T], где T расширяет A.Он передается в качестве аргумента для foldr, который возвращает тип функции, которая была ему передана (PlayList[T], где T расширяет A).И это должно быть какого-то типа PlayList[B].

Так что я предполагаю, что this:PlayList[B] необходимо, чтобы указать, что T и B связаны.

Может быть, вам нужно, чтобы PlayList был параметрическим в двух типах PlayList[+A, B >: A], поскольку у вас есть prepNode и propList, которые, кажется, работают с одним и тем же типом, расширяющим A?

Сказано иначе, ваш оригиналопределение класса можно было бы определить так:

def prepNode[T >: A](b: T): PlayList[T]
def prepList[U >: A](prefix: PlayList[U]): PlayList[U]

Но вы использовали B в обоих случаях, и компилятор не знает, что T и U одинаковы.

Редактируйте, вы можете поиграться с опцией -explaintypes и посмотреть, что делает компилятор в зависимости от подсказок типа, которые вы получаете.Вот вывод объясненных типов и удаление :PlayList[B] (с 2.8.0.RC1):

$ scalac -explaintypes -d classes Infer.scala
found   : node.type (with underlying type B)
 required: A
    prefix.foldr(this)((node, suffix) => suffix.prepNode(node))
                                                         ^
node.type <: A?
  node.type <: Nothing?
    B <: Nothing?
      <notype> <: Nothing?
      false
      Any <: Nothing?
        <notype> <: Nothing?
        false
      false
    false
  false
  B <: A?
    B <: Nothing?
      <notype> <: Nothing?
      false
      Any <: Nothing?
        <notype> <: Nothing?
        false
      false
    false
    Any <: A?
      Any <: Nothing?
        <notype> <: Nothing?
        false
      false
    false
  false
false

Надеюсь, это поможет пролить свет.Может быть вопрос вокруг, когда скаляр может вывести, а когда не может вывести, было бы полезно.

1 голос
/ 28 июня 2010

Проблема в том, что foldr не указывает B >: A, поэтому, что касается foldr, между его собственными типами A и B нет никакой связи. * * * * * * * * * * * * * * * * * * * * * * * * * *

* * * * * * * * * *1009* * * * * * * *1009* * * * * * *1009* * * * * * * * *1009* * * * * * * * * * * * * * * * * *1009* * * *
...