Type System of Scala: Covariant, Contravariant, and PECS Rule

Type System

What are types:

  • 意义的逻辑含义
  • 一个契约,要求所有成员有共同的特性
  • 所有值、变量都有type
  • 所有对象object都有type
  • 所有class、trait都type

value types & reference types

  • 有9种value types,它们不能为空:Byte Short Char Int Long Float Double Boolean Unit
  • 其他都是reference types:objects functions 等等

Type Hierarchies

image-20211022140614737
image-20211022140614737

WEAK CONFORMANCE

image-20211022140142393
image-20211022140142393
  • 和Java类似,前面type的都可以赋值转化成后面的type(typecast)
  • 数、字符字面量,都可以赋值给 Byte Short and Char

Subtype

  • 子类可以被当做父类

    1
    val s: Student = new SmartStudent // 如此可以。s仍然是一个Student类,但它拥有SmartStudent的属性和方法
  • S是T的子类,记作\(𝑆 <∶ 𝑇\), 读作 S is a T and satisfies all properties that T does,so S can be treated as T

可以把S赋值给T的三种情况:

  1. 𝑆 primitive-widens to 𝑇 : widening is like Int converted to Double, is done implicitly
  2. 𝑆 primitive-narrows to 𝑇 : narrowing is like Double to Int, needs conversion
  3. 𝑆 <∶ 𝑇

Type Inference

Type inference is a technique for determining the type of some entity without explicitly specifying it

谨慎使用🤔

GENERICS, PARAMETRIC POLYMORPHISM & VARIANCE

想要通用化一个程序,可能是需要它可以处理不同的类型的参数。那么就可以把参数类型也作为一个参数传入,就是type parameter。表示为:

[T]

这样就会遇到subtype定义的问题。==a box of T should be able to both store and produce a T.==

假如GummyBear是Sweet的子类,但下面这两个都是错的:

1
2
3
4
5
6
7
8
9
val b: Box[GummyBear] = new Box[Sweet](new Sweet) // wrong
// 因为上述b需要可以 存 且 提供box中的类:
b.value = new GummyBear // ok
val a: GummyBear = b.value // not ok!

val b: Box[Sweet] = new Box[GummyBear](new GummyBear) // wrong
// 因为上述b需要可以 存 且 提供box中的类:
b.value = new Sweet // not ok!
val a: Sweet = b.value // ok

PECS Rule - Producer: Extends; Consumer: Super

  1. if you need a box that Produces T, use a box of anything that Extends T

    1
    def produceFromBox(b: Box[_ <: Sweet]): Sweet = b.value

    covariant: \[ \frac{S <: T}{C[S] <: C[T]} \]

    1
    2
    class Box[+T](val value: T) // make Box covariant in T
    val b: Box[Sweet] = new Box[GummyBear](new GummyBear) // ok now
  2. if you need a box that Consumes T, use a box of anything that is Super of T

    1
    2
    3
    def putInBox(b: Box[_ >: GummyBear], s: GummyBear) = { 
    b.value = s
    }

    contravariant: \[ \frac{S <: T}{C[T] <: C[S]} \]

    1
    2
    3
    4
    class Monster[-T] { // make Monster contravariant in T
    def eat(t: T) = println(t)
    }
    val m: Monster[GummyBear] = new Monster[Sweet] // ok now

VARIANCE OF FUNCTION TYPES

\[ \frac{I_2<:I_1,R_2<:R_1}{I_1=>R_1<:I_2=>R_2} \]

for example, 一个function接收AnyRef,返回Subtring,那它就是这个function的子类:接收Sweet(<: AnyRef),返回String(>: Substring):

1
AnyRef => Substring <: Sweet => String