Почему мы должны явно указывать класс типов ClassTag

Теперь, когда scala перешел к исправлению стирания типа JVM с помощью ClassTag класс типов, почему он включен, вместо того, чтобы компилятор всегда захватывал сигнатуру типа для проверки во время выполнения. Наличие неявного параметризованного ограничения типа позволило бы вызвать classTag[T] независимо от объявления общего параметра.

РЕДАКТИРОВАТЬ: Я должен уточнить, что я не имею в виду, что Scala должен изменить подпись за кулисами, чтобы всегда включать ClassTag, Скорее я имею в виду, что с ClassTag показывает, что scala может собирать информацию о типе во время выполнения и, следовательно, избегать ограничений на стирание типов, почему этот захват не может быть неявным как часть компилятора, чтобы эта информация всегда была доступна в коде scala?

Я подозреваю, что это обратная совместимость, совместимость с Java-экосистемой, размер двоичного файла или накладные расходы времени выполнения, но это всего лишь предположение.

2 ответа

Решение

Обратная совместимость будет полностью разрушена, правда. Если у вас есть простой метод, такой как:

def foo[A](a: A)(implicit something: SomeType) = ???

Затем предположим, что в следующей версии Scala компилятор неожиданно добавил неявное ClassTagс подписью всех методов с параметрами типа. Этот метод будет сломан. Везде это явно называлось как foo(a)(someTypeValue) не будет работать больше Двоичная и исходная совместимость исчезла бы.

Совместимость Java была бы ужасной. Предположим, наш метод теперь выглядит так:

def foo[A : ClassTag](a: A) = ???

Так как ClassTags генерируются компилятором Scala, использование этого метода из Java оказалось бы более сложным. Вы должны создать ClassTag сам.

ClassTag<MyClass> tag = scala.reflect.ClassTag$.MODULE$.apply(MyClass.class);
foo(a, tag);

Моя Java может быть не на 100% правильной, но вы поняли идею. Все параметризованное станет очень уродливым. Ну, это уже так, если это требует неявного ClassTag, но класс методов, где это было бы необходимо, резко увеличился бы.

Более того, стирание типов не является большой проблемой в большинстве параметризованных методов, которые мы (по крайней мере, я) используем. Я думаю, что автоматически требует ClassTag для каждого параметра типа было бы гораздо больше проблем, чем помогло бы, по вышеуказанным причинам.

Конечно, это добавит больше накладных расходов компилятора, так как потребуется генерировать больше ClassTagчем обычно. Я не думаю, что это добавит намного больше времени выполнения, если только ClassTag имеет значение. Например, в простом методе, как показано ниже, ClassTag ничего не делает

def foo[A : ClassTag](a: A): A = a

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

val list = List(1, "abc", List(1, 2, 3), List("a", "b"))
def find[A: ClassTag](l: List[Any]): Option[A] =
    l collectFirst { case a: A => a }

scala> find[List[String]]
res2: Option[List[String]] = Some(List(1, 2, 3)) // Not quite! And no warnings, either.

Добавление ClassTag к каждому отдельному экземпляру класса добавляются накладные расходы и, конечно, также нарушается совместимость. Это также во многих местах невозможно. Мы не можем просто влить java.lang.String с ClassTag, Кроме того, мы все равно были бы так же подвержены стиранию. Иметь ClassTag поле в каждом классе действительно не лучше, чем с помощью getClass, Мы могли бы сделать сравнение, как

case a if(a.getClass == classOf[String]) => a.asInstanceOf[String]

Но это ужасно уродливо, требует актерский состав, и не обязательно то, что ClassTag предназначен для исправления. Если бы я попробовал что-то подобное с моим find метод, это не сработает - вообще.

// Can't compile
def find[A](l: List[Any]): Option[A] =
    l collectFirst { case a if(a.getClass == classOf[A]) => a.asInstanceOf[A] }

Даже если бы я сделал это, чтобы как-то работать с ClassTagоткуда это взялось? Я не мог сказать a.classTag == classTag[A], так как A уже был стерт. мне нужно ClassTag на сайте вызова метода.

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

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