OO, Variance, Type Safety









                 http://godfat.org/slide/2013-11-07-variance/

Variance?

Wikipedia says...

Within the type system of a programming language, a typing rule or a type constructor is:

Subtype?

Wikipedia says...




...typically subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype.

Variance where it comes:

Variance where it comes:

Invariant (mutable) Array


final class Array[T] extends ...

Java has covariant array which is not type safe

ArrayStoreException: Integer


public class Unsafe{
  public static void main(String[] args){
    String[] a = new String[1];
    Object[] b = a;
    b[0] = 1; // HERE!
    System.out.println(b[0]);
  }
}

Covariant (immutable) List


sealed abstract class List[+A] extends ...

If Animal < Cat, then

List[Animal] < List[Cat]


val animals: List[Animal] = List(new Cat)

Covariant (immutable) List


sealed abstract class List[+A] extends ...

Nonsense:


val what: List[Any] = 1 :: "hey" :: List()
val cats: List[Cat] = List(new Cat)
cats.contains(1) // false

Covariant (immutable) List


sealed abstract class List[+A] extends ...

Arguments can't be covariant


  def contains(elem: Any): Boolean

Null is a subclass of everything


val cat: Cat = null
val cats: List[Cat] = List(null)

But actually not really


null.isInstanceOf[Cat] // false

Variance where it comes:



Covariant Return Type



Contravariant Argument Type

Given base class and self:


class Animal              {
  def self: Animal = this }

self could be overridden with:


class Cat extends Animal  {
  override
  def self:    Cat = this }

It is safe as you can see...


val a1: Animal = new Cat
val a2: Animal = a1.self

And this works...


val c1:    Cat = new Cat
val c2:    Cat = c1.self



Covariant Return Type



Contravariant Argument Type

This is not allowed in Scala and most languages:


class Animal                       {
  def eat(food: Animal): Unit = () }

class Cat extends Animal           {
  override // Error: override nothing
  def eat(food: Object): Unit = () }

Shall be safe as we ask less tho.

Maybe it's just useless:


(new Cat).eat(new Object)

If we have more, why ask less?

Type safety, maybe.

Back for Function1:

Mnemonic:



Return more when asked;
Take less when given.

Or...

a > a

- > +

Example:


val f: Function1[Cat, Animal] =
   (a: Animal) => new Cat

with Haskell syntax:
(but it won't type check)


f :: Cat -> Animal
f = (\animal -> Cat) :: Animal -> Cat

That is,



(Cat -> Ani) < (Ani -> Cat)

Given:


trait Function1[-T, +R] extends AnyRef

If T1 > T2 (-), and R1 < R2 (+)

(T1 -> R1) < (T2 -> R2)

(Cat -> Ani) < (Ani -> Cat)

Uncurried Function2

.(a, a) > a

-(a, a) > +

.(-, -) > +

Example:


trait Function2[-T1, -T2, +R] extends AnyRef



It's the same thing

Curried Function

a > (a > a)

- > (- > +)

Example:


trait Function1[-T1, Function1[-T2, +R]]



It's the same thing

Higher-order Function?

.(a > a)>a

-(- > +)>+

.(+ > -)>+

Example:


val f: Function1[Function1[Animal, Cat], Animal] =
             (g: Function1[Cat, Animal]) => new Cat

                            +  ->  -  ->  +

References:

Q?