Default value for generic data structure
default-value, generics, scala
Solution
You could leverage the fact that Scala already provides you a way of getting the default value of a type. When you write `var x: Int = _`, this initialises `x` to `0`. Similar for all `AnyVal` types. All `AnyRef` types are initialised to `null`.
Bearing that in mind, you could rewrite your sparse vector class as following:
class SparseVector[T](val size: Int) {
import scala.collection.mutable.Map
private var default: T = _
private[this] val storage = Map[Int, T]()
def apply(key: Int) =
if(key < size)
storage.getOrElse(key, default)
else
throw new IllegalArgumentException("Index " + key + " out of bounds")
def update(key: Int, value: T) { storage(key) = value }
}
Now code like the following works as expected:
scala> val b = new SparseVector[Boolean](10)
b: SparseVector[Boolean] = SparseVector@cfd22a
scala> b(1)
res20: Boolean = false
scala> b(1) = true
scala> b(1)
res22: Boolean = true
scala> val i = new SparseVector[Int](10)
i: SparseVector[Int] = SparseVector@1813c12
scala> i(1)
res23: Int = 0
scala> i(1) = 10
scala> i(1)
res25: Int = 10
scala> i(10)
java.lang.IllegalArgumentException: Index 10 out of bounds
A couple of improvements I might make to this class:
- Have a `toString` method printing the collection in a reasonable way
- Provide a companion object which can change the default value of the vector if required (see the code below).
object SparseVector {
def apply[T](size: Int) = new SparseVector[T](size)
def apply[T](size: Int, default: T) = {
val result = new SparseVector[T](size)
result.default = default
result
}
}
Now this works:
scala> val b = SparseVector[Boolean](10, true)
b: SparseVector[Boolean] = SparseVector@126f29f
scala> b(4)
res28: Boolean = true
scala> val i = SparseVector[Int](10, 42)
i: SparseVector[Int] = SparseVector@b9979b
scala> i(3)
res30: Int = 42
EDIT: The code I have written works with Scala 2.7.6.final. Mitch Blevins has pointed out that the code yields `null` as a default value for `AnyVal` types when run with Scala 2.8r.19890. As explained in the comments, this should not be possible as `Null` is not a subtype of `AnyVal`.The general idea should be similar if using 2.8, as `var b: Boolean = _` should still give you the default value of the `Boolean` type. The usage of collections to store the sparse vector might be different, but as I said in the comment, I am not familiar with the 2.8 collection redesign.
EDIT2: ... the `null` behaviour should not be possible, but unfortunately it is. Doing some more research into the problem it seems that due to type erasure the field `default` always gets initialised to `null`. And after that... weirdness ensues. See Mitch's post for a discussion and some bear-bones code reproducing the problem.
Things I have tried and failed in order to make the code work as it should:
- `null.asInstanceOf[T]` - nope, Java doesn't have reified generics. This still yields `null`
- `@specialised` - nope, it seems that even if the compiler generates specialised code for primitives, you still get the null behaviour
- Casting the result to an `AnyVal`, which should not be `null`. Nope. Still `null`.
So conceptually, my solution should work. But it doesn't due to very strange behaviour which I have reported in the Scala Trac.
See also this blog post for a nice discussion of `null`able `AnyVal`s.
-- Flaviu Cipcigan
Problem
I would like to write a `SparseVector[T]` class where `T` can be a double, an int or a boolean. The class will not be backed by an array (because I want a sparse data structure) but I have seen that when I build an empty array of an `AnyVal` type, the elements are initialized to the default value. For instance: ``` scala> new Array[Int](10) res0: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) scala> new Array[Boolean](10) res1: Array[Boolean] = Array(false, false, false, false, false, false, false, false, false, false) scala> new Array[Double](10) res2: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) ``` How can I include this default value in my class ? The behaviour I would like to get is: ``` val v = new SparseVector[Double](100) println( v(12) ) // should print '0.0' val w = new SparseVector[Boolean](100) println( v(85) ) // should print 'false' ``` Thanks