Scala Question

How to Run and Different Code Paths Based on Specialisation

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)
}
}

Answer

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.

Comments