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

Original source