"Тип параметра в структурном уточнении может не относиться к абстрактному типу, определенному вне этого уточнения"

Когда я компилирую:

object Test extends App {
  implicit def pimp[V](xs: Seq[V]) = new {
    def dummy(x: V) = x
  }
}                                                                                                                                                                                                              

Я получил:

$ fsc -d aoeu go.scala
go.scala:3: error: Parameter type in structural refinement may not refer to an abstract type defined outside that refinement
    def dummy(x: V) = x
        ^
one error found

Зачем?

( Scala: "Тип параметра в структурном уточнении может не относиться к абстрактному типу, определенному вне этого уточнения", на самом деле это не отвечает.)

2 ответа

Решение

Это запрещено спецификацией. См. 3.2.7 Типы соединений.

В объявлении метода в структурном определении тип любого параметра-значения может ссылаться только на параметры типа или абстрактные типы, содержащиеся в этом определении. То есть он должен ссылаться либо на параметр типа самого метода, либо на определение типа в пределах определения. Это ограничение не распространяется на тип результата функции.

До исправления ошибки 1906 компилятор скомпилировал бы это, и вы получили бы метод, не найденный во время выполнения. Это было исправлено в ревизии 19442, и именно поэтому вы получили это замечательное сообщение.

Тогда возникает вопрос: почему это не разрешено?

Вот очень подробное объяснение Жиля Дубоче из списка рассылки scala в 2007 году. Оно примерно сводится к тому, что структурные типы используют отражение, и компилятор не знает, как искать вызываемый метод, если он использует тип, определенный снаружи. уточнение (компилятор не знает заранее, как заполнить второй параметр getMethod в p.getClass.getMethod("pimp", Array(?))

Но посмотрите на пост, он ответит на ваш вопрос и еще немного.

Редактировать:

Привет список

Я пытаюсь определить структурные типы с абстрактным типом данных в параметре функции.... Какой-либо причине?

В последнее время я слышал о двух вопросах, касающихся расширения структурной типизации Scala 2.6, и я хотел бы ответить на них здесь.

  1. Почему мы изменили схему упаковки собственных значений Scala ("int" и т. Д.) На Java ("java.lang.Integer").
  2. Почему требуется ограничение параметров для структурно определенных методов ("Тип параметра в структурном уточнении может не относиться к абстрактному типу, определенному вне этого же уточнения").

Прежде чем я смогу ответить на эти два вопроса, мне нужно поговорить о реализации структурных типов.

Система типов JVM очень проста (и соответствует Java 1.4). Это означает, что многие типы, которые могут быть представлены в Scala, не могут быть представлены в ВМ. Зависимые от пути типы ("xyA"), одноэлементные типы ("a.type"), составные типы ("A с B") или абстрактные типы - это все типы, которые не могут быть представлены в системе типов JVM.

Чтобы иметь возможность компилировать в байт-код JVM, компиляторы Scala изменяют типы программы Scala на "стирание" (см. Раздел 3.6 справки). Стираемые типы могут быть представлены в системе типов ВМ и определяют дисциплину типов в программе, которая эквивалентна дисциплине программы, типизированной типами Scala (с сохранением некоторых приведений), хотя и менее точной. В качестве примечания, тот факт, что типы стираются в ВМ, объясняет, почему операции по динамическому представлению типов (сопоставление с образцом в типах) очень ограничены в отношении системы типов Scala.

До сих пор все конструкции типов в Scala могли быть каким-то образом удалены. Это не верно для структурных типов. Простой структурный тип "{ def x: Int }" нельзя стереть в "Object", так как виртуальная машина не позволяет получить доступ к полю "x". Использование интерфейса "interface X { int x{}; } ", Так как стертый тип также не будет работать, потому что любой экземпляр, связанный со значением этого типа, должен будет реализовать этот интерфейс, что невозможно сделать при наличии отдельной компиляции. Действительно (смиритесь со мной) любой класс, который содержит член с тем же именем, что и член, определенный в структурном типе в любом месте, должен был бы реализовать соответствующий интерфейс. К сожалению, этот класс может быть определен еще до того, как станет известно о существовании структурного типа.

Вместо этого любая ссылка на структурно определенный член реализована как рефлексивный вызов, полностью обходя систему типов виртуальной машины. Например def f(p: { def x(q: Int): Int }) = p.x(4) будет переписан что-то вроде:

  def f(p: Object) = p.getClass.getMethod("x", Array(Int)).invoke(p, Array(4))

А теперь ответы.

  1. "Invoke" будет использовать упакованные значения ("java.lang.Integer") всякий раз, когда вызываемый метод использует собственные значения ("int"). Это означает, что приведенный выше вызов должен действительно выглядеть как "...invoke(p, Array(new java.lang.Integer(4))). IntValue".

Целочисленные значения в программе Scala уже часто упаковываются (чтобы разрешить тип "Любой"), и было бы расточительно распаковывать их из собственной схемы бокса Scala, чтобы сразу же перезаписать их как java.lang.Integer.

Что еще хуже, когда рефлексивный вызов имеет тип возврата "Any", что нужно делать, когда возвращается java.lang.Integer? Вызываемый метод может либо возвращать "int" (в этом случае он должен быть распакован и перезаписан как поле Scala), либо он может возвращать java.lang.Integer, который следует оставить нетронутым.

Вместо этого мы решили изменить собственную схему бокса в Scala на Java. Две предыдущие проблемы просто исчезают. Некоторые оптимизации, связанные с производительностью, которые мы имели со схемой бокса в Scala (предварительно рассчитав коробочную форму наиболее распространенных чисел), также были просты в использовании с боксом Java. В конце концов, использование Java-бокса оказалось даже немного быстрее, чем наша собственная схема.

  1. Второй параметр "getMethod" - это массив с типами параметров (структурно определенного) метода для поиска - для выбора метода, который нужно получить, когда имя перегружено. Это единственное место, где необходимы точные статические типы в процессе перевода вызова структурного члена. Обычно используемые статические типы для параметра метода предоставляются с определением структурного типа. В приведенном выше примере тип параметра "x" известен как "Int", что позволяет искать его.

Типы параметров, определенные как абстрактные типы, где абстрактный тип определяется внутри области структурного уточнения, также не являются проблемой: def f(p: { def x[T](t: T): Int }) = p.xInt В этом Например, мы знаем, что любой экземпляр, переданный в "f" как "p", будет определять "x [T] (t: T)", который обязательно стирается в "x(t: Object)". Поиск выполняется правильно для стертого типа: def f(p: Object) = p.getClass.getMethod("x", Array(Object)). Invoke (p, Array (new java.lang.Integer (4)).))

Но если для определения параметра структурного метода используется абстрактный тип вне области структурного уточнения, все ломается: def f[T](p: { def x(t: T): Int }, t: T) = px(t) Когда вызывается "f", "T" может быть создан для любого типа, например: f[Int]({ def x(t: Int) = t }, 4) f[Any]({ def x(t: Any) = 5 }, 4) Поиск для первого случая должен быть "getMethod("x", Array(int))", а для второго "getMethod (" x ", Array (Object))) ", И нет способа узнать, какой из них генерировать в теле" f ":" px(t) ".

Чтобы разрешить определение уникального вызова "getMethod" внутри тела "f" для любого экземпляра "T", потребуется, чтобы любой объект, переданный в "f" в качестве параметра "p", имел тип "t", стертый в "Any". ". Это было бы преобразованием, когда тип членов класса зависит от того, как экземпляры этого класса используются в программе. И это то, что мы определенно не хотим делать (и не можем сделать с отдельной компиляцией).

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

Но пока использование абстрактных типов для типов параметров структурного метода просто запрещено.

С уважением, Жиль.

Обнаружил проблему вскоре после публикации: я должен определить именованный класс вместо использования анонимного класса. (Тем не менее, хотелось бы услышать лучшее объяснение причин.)

object Test extends App {
  case class G[V](xs: Seq[V]) {
    def dummy(x: V) = x
  }
  implicit def pimp[V](xs: Seq[V]) = G(xs)
}                                                                                                                                                                                                              

работает.

Другие вопросы по тегам