Специализация Scala ArrayBuilder без использования котлов

Я ищу хорошие практики, чтобы избежать переписывания одного и того же кода снова и снова для достижения распаковки. Скажем, у меня есть что-то вроде этого:

def speedyArrayMaker[@specialized(Long) A: ClassTag](...): Array[A] = {
  val builder = Array.newBuilder[A]
  // do stuff with builder
  builder.result
}

Это приведет к распаковке хранилища под моим builder когда это возможно, но, насколько я понимаю, без вызова метода не вызывает его, потому что я прохожу через специализированный ArrayBuilder черта характера.

В мономорфном мире, специализирующемся на LongЯ бы написал val builder = new ArrayBuilder.ofLong() и вообще избегать бокса, но не сказать ArrayBuilder/Builder специализируясь на всех примитивных типах, я не могу придумать хороший способ избежать дублирования усилий здесь. Один из подходов, о котором я подумал, может заключаться в speedyArrayMaker:

val (add, builder): (A => Unit, ArrayBuilder[A]) = implicitly[ClassTag[A]].runtimeClass match {
  case java.lang.Long.TYPE =>
    val builder = new ArrayBuilder.ofLong()
    ((x: Long) => builder += x, builder).asInstanceOf
  case _ =>
    val builder = Array.newBuilder[A]
    ((x: A) => builder += x, builder)
}

Так как это только += метод, который мы действительно хотим, чтобы стать специализированным, а затем мы получаем Function1 за add который специализируется на Long, Проверяю с помощью javap, действительно получаю

90:  invokestatic    #118; //Method scala/runtime/BoxesRunTime.boxToLong:(J)Ljava/lang/Long;
93:  invokeinterface #127,  2; //InterfaceMethod scala/collection/mutable/Builder.$plus$eq:(Ljava/lang/Object;)Lscala/collection/mutable/Builder;

для Array.newBuilder[A] версия (даже в специализированном выводе) и:

252: invokeinterface #204,  3; //InterfaceMethod scala/Function1.apply$mcVJ$sp:(J)V

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

Есть ли хитрые уловки, которые я могу использовать, чтобы сделать это более приятным? Я понимаю, что это действительно низкоуровневые детали, и они редко будут критичными для производительности, но учитывая количество усилий / дублирования кода, которые были вложены во все ArrayBuilder.of* Для специализированных классов, кажется, жалко отбрасывать некоторые их преимущества в обмен на полиморфность.

Править Я думал о чем-то уродливом, но надеялся, что это сработает:

def builderOf(x: Array[Int]): ArrayBuilder.ofInt = new ArrayBuilder.ofInt()
def builderOf(x: Array[Long]): ArrayBuilder.ofLong = new ArrayBuilder.ofLong()
def builderOf[A: ClassTag](x: Array[A]): ArrayBuilder[A] = ArrayBuilder.make[A]

а затем внутри моей специализированной функции:

val witness: Array[A] = null
val builder = builderOf(witness)

но, кажется, называют общим builderOf даже в специализированной версии (хотя имеется достаточно информации о типе для вызова Array[Long] версия). Кто-нибудь знает, почему это не работает? Подход кажется довольно чистым по сравнению с другим, который я предлагал. Думаю, я надеялся на более "макроподобный" подход к специализации, но, думаю, нет гарантии, что он будет корректным по типу для всех экземпляров, если не выберет один и тот же метод для каждой специализации:(

1 ответ

Вы можете попробовать что-то вроде (извините за варварские имена),

import scala.collection.mutable.ArrayBuilder
import scala.reflect.ClassTag

trait SpecializedArrayBuilder[@specialized(Long) A] {
  def +=(a: A)
  def result: Array[A]
}

trait LowPrioritySpecializedArrayBuilder {
  implicit def defaultBuilder[A: ClassTag] = new SpecializedArrayBuilder[A] {
    val builder = ArrayBuilder.make[A]
    def +=(a: A) = builder += a
    def result = builder.result
  }
}

object SpecializedArrayBuilder extends LowPrioritySpecializedArrayBuilder {
  implicit val longBuilder = new SpecializedArrayBuilder[Long] {
    val builder = new ArrayBuilder.ofLong
    def +=(a: Long) = builder += a
    def result = builder.result
  }
}

object Specialized {
  def speedyArrayMaker[@specialized(Long) A](a: A)
    (implicit builder: SpecializedArrayBuilder[A]): Array[A] = {
    builder += a
    builder.result
  }

  def main(arg: Array[String]) {
    val arr = speedyArrayMaker(1L)
    println(arr)
  }
}
Другие вопросы по тегам