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:
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:
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
...
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:
- class Verified[+A <: V,V](assertion : (V) => Boolean, private var value : A){
- assert(assertion(value))
-
- def a = value
- // update is illegal. See the example below
- def update[ B >: A <: V](a : B) = value = a
- }
- def notNull(obj : AnyRef) = obj != null
- val v = new Verified(notNull, "hi")
- /*
- Up to this point everything looks ok but the next line
- will assign an object to value which is a reference to a String
- */
- v 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:
- scala> class X[+A](val x :A)
- defined class X
- scala> class Y[A](var a: A) extends X[A](a)
- defined class Y
- scala> val x: X[Any] = new Y[String]("hi")
- x: X[Any] = Y@1732a4df
- 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
- scala> def printList(l : List[Any]) = print(l mkString " :: ")
- printList: (l: List[Any])Unit
- scala> val buffer = Buffer(1,2,3)
- buffer: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)
- scala> printList(buffer)
- 1 :: 2 :: 3
- /*
- ++ is part of Iterable. Since Iterable is covariant ++
- returns a new buffer it does not modify the existing buffer
- All mutators are only defined on invariant traits
- */
- scala> buffer ++ List(4)
- res16: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3, 4)
- scala> res16 eq buffer
- res17: Boolean = false
- /*
- buffer defines += to add an element to the buffer
- so res27 is the same buffer with an extra element
- */
- scala> buffer += 10
- res27: buffer.type = ArrayBuffer(1, 2, 3, 10)
- scala> res27 eq buffer
- res28: Boolean = true
posted
3/31/2010
Labels:
collections,
covariance,
intermediate,
invariance,
Scala,
variance
Subscribe to:
Post Comments (Atom)
1 comment:
seriously, this series has been blowing my mind a little bit.
Reply Deletethanks.