Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
## Covariance and Contravariance Illustrated
import Experiments_1.Q2
object Experiments_1:
// class Q1[+T]:
// def enqueue(x: T): Unit = println(s"Q1 enqueued $x")
// val x1: Q1[Any] = new Q1[Int]
class Q2[-T]:
def enqueue(x: T): Unit = println(s"Q2 enqueued $x")
val x2: Q2[Int] = new Q2[Any]
class StrangeIntQ2 extends Q2[Int]:
override def enqueue(x: Int) = println(s"SQ2 eenqueued $x")
class Q3[T]:
def enqueue(x: T): Unit = println(s"Q2 enqueued $x")
// val x31: Q3[Any] = new Q3[Int]
// val x32: Q3[Int] = new Q3[Any]
class StrangeIntQ3 extends Q3[Int]:
override def enqueue(x: Int) = println(s"SQ3 Eenqueued $x")
class WeirdIntQ extends StrangeIntQ3:
val flag = true
def main(args: Array[String]): Int = {
// val sq2: Q2[Any] = new StrangeIntQ2
// sq2.enqueue("abc")
// val sq3: Q3[Any] = new StrangeIntQ3
// sq3.enqueue("abc")
val x5: StrangeIntQ3 = new WeirdIntQ
object VarianceExamples:
trait A;
trait B extends A;
trait C extends B;
val a = new A{}
val b = new B{}
val c = new C{}
class Co[+X](x: X):
def give: X/*+*/ = x
val coABc: Co[A] = Co[B](c) // covariant assignment
val coBBc: Co[B] = Co[B](c) // same type parameter
val coBBa: Co[B] = Co[B](a) // canot pass an A when a B is required
val coBAb: Co[B] = Co[A](b) // cannot assign a Co[A] to Co[B] (variance)
val coCBa: Co[C] = Co[B](a) // cannot assign a Co[B] to Co[C] (variance)
class Contra[-X](var x: X):
def give: X/*+*/ = x
def take[Y <: X](x1: X/*-*/): String = { x = x1; "wow" }
val conABa: Contra[A] = Contra[B](a) // requires a B as a constructor param
val conABb: Contra[A] = Contra[B](b) // covariant conversion not working here
val conBAa: Contra[B] = Contra[A](a)
val bbb: B = conBAa.give // conBAa does not contain a B! will crash in runtime
val conCAa: Contra[C] = Contra[A](b)
val conCBa: Contra[C] = Contra[B](b)
val cocoAB: Co[Co[A]] = Co[Co[B]](coBBc) // co • co = co
val cocoCB: Co[Co[C]] = Co[Co[B]](coBBc) // should go up the hierarchy
val coconCB1: Co[Contra[C]] = Co[Contra[B]](conBAa) // contra • co = contra
val coconAB1: Co[Contra[A]] = Co[Contra[B]](conBAa) //should go down the hierarchy
val coconCB2: Contra[Co[C]] = Contra[Co[B]](coBBc) // co • contra = contra
val coconAB2: Contra[Co[A]] = Contra[Co[B]](coBBc) // should go down the hierarchy
val conConAB: Contra[Contra[A]] = Contra[Contra[B]](conBAa) // contra • contra = co
val conConCB: Contra[Contra[C]] = Contra[Contra[B]](conBAa) // should go up
class Cat[T, +U]:
// def meow[W-](volume: T-, listener: Cat[U+, T-]-): Cat[Cat[U+, T-]-, U+]+
def meow[W ](volume: T , listener: Cat[U , T ] ): Cat[Cat[U , T ] , U ] = ???
trait X
trait Y extends X
trait Z extends Y
val x = new X{}
val y = new Y{}
val z = new Z{}
val catYB = new Cat[Y, B]
val catYA: Cat[Y, A] = catYB
val catYC: Cat[Y, C] = new Cat[Y, C]
trait K
trait L extends K
trait M extends L
val c1: Cat[Cat[B, Y], B] = catYB.meow[L](y, Cat[B, Y]) // standard, legal
val c2: Cat[Cat[A, Y], A] = catYB.meow[L](y, Cat[A, Y]) // First argument has no variance!
val c3: Cat[Cat[A, Y], A] = catYA.meow[L](y, Cat[A, Y])
val c4: Cat[Cat[A, Y], A] = catYA.meow[M](y, Cat[A, Y]) // ok: contravariant by W
val c5: Cat[Cat[A, Y], A] = catYA.meow[L](y, Cat[A, Z]) // ok: contra o contra
val c6: Cat[Cat[A, Y], A] = catYA.meow[L](y, Cat[A, X]) // ok: contra o contra
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment