Wednesday, March 31, 2010

Variant Positions 2

This is a continuation of post: Variant Positions 1

...

My first attempt at the Verified was to make it a mutable object (my Java tourettes kicking in). But it cannot be covariant and mutable. Look at the code to see why:
  1. class Verified[+A <: V,V](assertion : (V) => Booleanprivate var value : A){
  2.     assert(assertion(value))
  3.     
  4.     def a = value
  5. // update is illegal.  See the example below
  6.     def update[ B >: A <: V](a : B) = value = a
  7. }
  8. def notNull(obj : AnyRef) = obj != null
  9. val v = new Verified(notNull, "hi")
  10. /*
  11. Up to this point everything looks ok but the next line
  12. will assign an object to value which is a reference to a String
  13. */
  14. update (new Object())

For Verified to be mutable A must be invariant. If you look at the Mutable collections in Scala they are all invariant.

Here is an interesting example of both invariant and covariant type parameters in a class hierarchy:
  1. scala> class X[+A](val x :A)
  2. defined class X
  3. scala> class Y[A](var a: A) extends X[A](a)
  4. defined class Y
  5. scala> val x: X[Any] = new Y[String]("hi")
  6. x: X[Any] = Y@1732a4df
  7. scala> x.asInstanceOf[Y[String]].a="ho"

This example is perfectly legal because no matter how X[Any] is used no illegal assignment in Y can occur. The interesting thing is that the object can be used in covariant usecases when only X is required. This is now the collections in Scala can work.

Here is a little example of collections invariance and covariance in action. In List the parameter is covariant but in Buffer it is invariant
  1. scala> def printList(l : List[Any]) = print(l mkString " :: ")
  2. printList: (l: List[Any])Unit
  3. scala> val buffer = Buffer(1,2,3)
  4. buffer: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)
  5. scala> printList(buffer)
  6. 1 :: 2 :: 3
  7. /*
  8. ++ is part of Iterable.  Since Iterable is covariant ++ 
  9. returns a new buffer it does not modify the existing buffer
  10. All mutators are only defined on invariant traits
  11. */
  12. scala> buffer ++ List(4)
  13. res16: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3, 4)
  14. scala> res16 eq buffer
  15. res17: Boolean = false
  16. /*
  17. buffer defines += to add an element to the buffer
  18. so res27 is the same buffer with an extra element
  19. */
  20. scala> buffer += 10
  21. res27: buffer.type = ArrayBuffer(1, 2, 3, 10)
  22. scala> res27 eq buffer
  23. res28: Boolean = true

1 comment:

  1. seriously, this series has been blowing my mind a little bit.

    thanks.

    ReplyDelete