Почему так мало вещей @specialized в стандартной библиотеке Scala?
Я искал использование @specialized
в исходном коде стандартной библиотеки Scala 2.8.1. Похоже, что только несколько черт и классов используют эту аннотацию: Function0
, Function1
, Function2
, Tuple1
, Tuple2
, Product1
, Product2
, AbstractFunction0
, AbstractFunction1
, AbstractFunction2
.
Ни один из классов коллекции не является @specialized
. Почему нет? Будет ли это генерировать слишком много классов?
Это означает, что использование классов коллекции с примитивными типами очень неэффективно, потому что будет много ненужного бокса и распаковки.
Каков наиболее эффективный способ иметь неизменный список или последовательность (с IndexedSeq
характеристиками) Int
s, избегая бокса и распаковки?
Ответы
Ответ 1
Специализация имеет высокую стоимость по размеру классов, поэтому ее необходимо добавить с тщательным рассмотрением. В конкретном случае коллекций я полагаю, что воздействие будет огромным.
Тем не менее, это постоянное усилие - библиотека Scala едва ли начала специализироваться.
Ответ 2
Специализированный может быть дорогим (экспоненциальным) как по размеру классов, так и по времени компиляции. Это говорит не только размер, как принятый ответ.
Откройте свой scala REPL и введите это.
import scala.{specialized => sp}
trait S1[@sp A, @sp B, @sp C, @sp D] { def f(p1:A): Unit }
Извините:-). Это похоже на компиляторную бомбу.
Теперь давайте рассмотрим простой признак
trait Foo[Int]{ }
Приведенное выше приведет к созданию двух скомпилированных классов. Foo, чистый интерфейс и Foo $1, реализация класса.
Теперь
trait Foo[@specialized A] { }
Специальный параметр шаблона здесь расширяется/перезаписывается для 9 различных примитивных типов (void, boolean, byte, char, int, long, short, double, float). Итак, в основном вы получаете 20 классов вместо 2.
Возвращаясь к признаку с 5 специализированными параметрами шаблона, классы генерируются для каждой комбинации возможных примитивных типов. т.е. его экспоненциальность по сложности.
2 * 10 ^ (нет специализированных параметров)
Если вы определяете класс для определенного примитивного типа, вы должны быть более явным в нем, например
trait Foo[@specialized(Int) A, @specialized(Int,Double) B] { }
Понятно, что нужно быть экономным, используя специализированные при создании библиотек общего назначения.
Здесь рассказывает Пол Филлипс.
Ответ 3
Частичный ответ на мой собственный вопрос: я могу обернуть массив в IndexedSeq
следующим образом:
import scala.collection.immutable.IndexedSeq
def arrayToIndexedSeq[@specialized(Int) T](array: Array[T]): IndexedSeq[T] = new IndexedSeq[T] {
def apply(idx: Int): T = array(idx)
def length: Int = array.length
}
(Конечно, вы все равно можете изменить содержимое, если у вас есть доступ к базовому массиву, но я бы удостоверился, что массив не передан в другие части моей программы).