0

In using a framework like FastUtils with Scala, how do you generate the appropriate code based on the effective specialization as the framework itself has specialised data structures? I.e., how you you programmatically figure out what is being specialized and execute the appropriate code? So how do you deal with path related typing in such cases.

For objects

class Container[@specialized T](var window: Int) {
  val data = new ObjectArrayList[T](window)
}

For char I want it to be:

class Container[@specialized T](var window: Int) {
  val data = new CharArrayList(window)
}

But this should be based on the specialization of T. If I am to put this differently the sudo code would be perhaps like

class Container[@specialized T](var window: Int) {
  val data = specialisationOf(T) match {
    case "Char" => new CharArrayList(window)
    case "Int" => new IntegerArrayList(window)
    ...
    ...
    ...
    case _ => new ObjectArrayList[T](window)
  }
}
  • I am already doing manual specialization. I want to do it is with minimal redefinitions. Thanks for the pointer through but this does not solve the problem I am after.. – Suminda Sirinath S. Dharmasena Oct 03 '16 at 13:40
  • It is not possible in the way you wrote in your question, since the different `ArrayList` types don't have a common supertype with specialized methods. On (almost) every method call to `data` you would have to do the `match` thing again and cast to the most specific type. And that probably just replaces boxing overhead for overhead of runtime manual type comparing. – Jasper-M Oct 03 '16 at 13:54

1 Answers1

1

As already explained in this question, you can encapsulate the specialized implementation in a typeclass. This would look more or less like the following code.

import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.chars.CharArrayList
import it.unimi.dsi.fastutil.objects.ObjectArrayList

class Container[@specialized(Int,Char) T](window: Int)(implicit impl: ContainerImpl[T]) {
  impl.init(window)

  def add(element: T) = impl.add(element)
  override def toString = impl.toString
}

trait ContainerImpl[@specialized(Int,Char) T] {
  def init(window: Int): Unit
  def add(element: T): Unit
  def toString: String
}

object ContainerImpl extends LowerPriorityImplicits {
  implicit def intContainer = new ContainerImplInt
  implicit def charContainer = new ContainerImplChar
}

trait LowerPriorityImplicits {
  implicit def anyContainer[T] = new ContainerImplT[T]
}

final class ContainerImplInt extends ContainerImpl[Int] {
  var data: IntArrayList = _
  def init(window: Int) = data = new IntArrayList(window)
  def add(element: Int) = data.add(element)
  override def toString = data.toString
}

final class ContainerImplChar extends ContainerImpl[Char] {
  var data: CharArrayList = _
  def init(window: Int) = data = new CharArrayList(window)
  def add(element: Char) = data.add(element)
  override def toString = data.toString
}

final class ContainerImplT[T] extends ContainerImpl[T] {
  var data: ObjectArrayList[T] = _
  def init(window: Int) = data = new ObjectArrayList(window)
  def add(element: T) = data.add(element)
  override def toString = data.toString
}

Do note that although the implementation of add always looks the same, the method being called on data is a different overload every time. If you would write this in a more polymorphic way, the most specific add method would not be chosen, and your Int or Char will need to be boxed.

Community
  • 1
  • 1
Jasper-M
  • 14,966
  • 2
  • 26
  • 37