Когда именно происходит стирание типа?

Синтаксис scala очень многообещающий. Я думал, что изначально scala - это больше, чем просто удобная java, и может ввести совершенно новую парадигму программирования, но многие функции, допускаемые синтаксисом, неверны с точки зрения семантики. Итак, я начал поиски ограничений scala и способов их устранения. Хотя я просто пишу тестовый проект, чтобы приспособить к scala способы и шаблоны с другой точки зрения.

Основное препятствие - стирание типов, унаследованное от реализации jvm. Я могу написать небольшую статью под названием «Десять красивых узоров, которые испортили стирание типа». Говорят, что стирание шрифтов портит обобщения, но я наткнулся на стирание типов в миксинах. Считаю это проблемой с реализацией миксина в scala.

Преамбула

trait T1
trait T2

trait B1 {
  def typeMe(x:T1){}
}

Сломанный код с миксинами

trait B2 extends B1 {
  def typeMe(x:T1 with T2) {}
}

Рабочий код с примесью, объявленной как отдельная черта

trait T3 extends T1 with T2
trait B3 extends B1 {
  def typeMe(x:T3) {}
}

Ошибка:

error: name clash between defined and inherited member:
method typeMe:(x: T1 with T2)Unit and
method typeMe:(x: T1)Unit in trait B1
have same type after erasure: (x: $line12.$read#$iw#$iw#T1)Unit
    def typeMe(x:T1 with T2)

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


Обновить:

Мне очень жаль, я задал не тот вопрос. Меня больше интересует природа описанной ошибки, а не способы ее решения. Где происходит стирание типа? Я не вижу универсальных шаблонов в коде, который считается источником стирания типов. Каков механизм поведения компилятора?


person ayvango    schedule 27.02.2012    source источник
comment
Вы можете рассмотреть альтернативу перегруженным методам; назовите их по-другому или используйте шаблон класса типов.   -  person Owen    schedule 28.02.2012
comment
Я надеюсь, вы понимаете, что в B3 вы не переопределяете метод typeMe из B1, а перегружаете его, чтобы он принял подтип. Хотя это законно, это плохая идея, поскольку вызываемый метод будет зависеть от типа локальной переменной. т.е. предполагая, что объект B3 b3 и объект T3 t3, b3.typeMe(t3) и b3.typeMe(t3: T1) будут вызывать разные методы.   -  person Luigi Plinge    schedule 28.02.2012


Ответы (1)


Есть пара проблем с тем, что вы пытаетесь сделать.

Во-первых, не имеет значения, когда происходит стирание типа. Это связано с тем, что концепция стирания типов заключается в том, что независимо от того, стираете ли вы типы до или после вычисления, результат должен быть одинаковым. Для языков JVM некоторое стирание происходит во время компиляции, iinm.

Во-вторых, ваш «рабочий» код на самом деле работает не так, как вы думаете. Это требует некоторого понимания подтипов, ковариации и контравариантности. (Foo <: Bar означает «Foo является подтипом Bar» или неофициально, что «Foo более специфичен, чем Bar»). Функции контравариантны на входе и ковариантны на выходе, что означает, что функция A => B <: W => V только если W <: A и B <: V.

Теперь рассмотрим свои функции:

B1.typeMe(x:T1)
B3.typeMe(x:T3)

Входной параметр B3.typeMe - T3, а тип входа B1.typeMe - T1. Однако T3 <: T1 (T3 более конкретен, чем T1), поэтому тип B3.typeMe не может быть подтипом типа B1.typeMe. Вместо этого они просто считаются отдельными функциями, иногда с некоторой жуткой бесшумной слежкой. Честно говоря, это, вероятно, должно вызвать ошибку или, по крайней мере, предупреждение. К счастью, есть другая версия.

person Dan Burton    schedule 28.02.2012
comment
Я написал не актуальный вопрос. Главный вопрос заключался в том, где происходит стирание типа, какие дженерики стоят за ошибкой. - person ayvango; 28.02.2012