Last active
August 24, 2021 03:15
-
-
Save vpatryshev/af2f1f95d37611d9351607ae63fac6de to your computer and use it in GitHub Desktop.
## Covariance and Contravariance Illustrated
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
42 | |
} | |
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