Wickoo Wickoo - 10 days ago 6
Scala Question

Adding @specialized tag causes "value is not a member" error in Scala

I have the following class definition for a resizable array in Scala:

class ResizeableArray[T: Manifest](initialCapacity: Int) {
private var capacity = initialCapacity
private var arr = Array.ofDim[T](capacity)
}


This definition works fine, but when I add
@specialized(Int)
to specialize for primitive type
Int
, I get the following strange error:

class ResizeableArray[@specialized(Int) T: Manifest](initialCapacity: Int) {
private var capacity = initialCapacity
private var arr = Array.ofDim[T](capacity)
}

<console>:15: error: value capacity is not a member of ResizeableArray$mcI$sp
private var arr = Array.ofDim[T](capacity)


Can someone explain to me what's going on here? I am using Scala 2.12.

Answer

According to the Specialized SID Section 3.1:

Not all members of a class are specialized. Currently, specialized variants of a member m are created only when m’s type contains at least one specialized naked type parameter, or an array of a naked specialized type parameter.

Normally, this might not make a difference, but it does here. This is because when you specialize ResizeableArray for Int, the compiler generates another class like this (note that var capacity isn't there):

  <specialized> class ResizeableArray$mcI$sp extends ResizeableArray {
    implicit <paramaccessor> private val evidence$1: scala.reflect.Manifest = _;
    <specialized> protected[this] var arr$mcI$sp: Array[Int] = _;
    <accessor> <specialized> protected def arr$mcI$sp(): Array[Int] = ResizeableArray$mcI$sp.this.arr$mcI$sp;
    override <accessor> <specialized> protected def arr(): Array[Int] = ResizeableArray$mcI$sp.this.arr$mcI$sp();
    <accessor> <specialized> protected def arr$mcI$sp_=(x$1: Array[Int]): Unit = ResizeableArray$mcI$sp.this.arr$mcI$sp = x$1;
    override <accessor> <specialized> protected def arr_=(x$1: Array[Int]): Unit = ResizeableArray$mcI$sp.this.arr$mcI$sp_=(x$1);
    def specInstance$(): Boolean = true;
    override <bridge> <specialized> <artifact> protected def arr_=(x$1: Object): Unit = ResizeableArray$mcI$sp.this.arr_=(x$1.$asInstanceOf[Array[Int]]());
    override <bridge> <specialized> <artifact> protected def arr(): Object = ResizeableArray$mcI$sp.this.arr();
    <specialized> def <init>(initialCapacity: Int, evidence$1: scala.reflect.Manifest): ResizeableArray$mcI$sp = {
      ResizeableArray$mcI$sp.this.evidence$1 = evidence$1;
      ResizeableArray$mcI$sp.super.<init>(initialCapacity, evidence$1);
      ResizeableArray$mcI$sp.this.arr$mcI$sp = scala.Array.ofDim(ResizeableArray$mcI$sp.this.initialCapacity(), evidence$1).$asInstanceOf[Array[Int]]();
      ()
    }
  }

By the above definition, only members that contain the specialized parameter in some way will be overridden in the implementation of ResizeableArray$mcI$sp. This means that capacity will not be overridden or implemented in the specialized class, so the compiler expects that it will be inherited instead, if needed. Unfortunately, since capacity is private, it is not inherited and you get a compiler error. I don't know if private members were overlooked here or consciously excluded, but you will need to take care when using them in specialzed classes.

In this case, a simple work-around will do:

class ResizeableArray[@specialized(Int) T: Manifest](initialCapacity: Int) {
  private var capacity = initialCapacity
  private var arr = Array.ofDim[T](initialCapacity)
}

The only other way around this would be to make capacity public, which surely you wouldn't want here, but may be acceptable in other cases (when it isn't mutable).