Demystifying Scala Type System

36
David Galichet CTO @ CoachClub Demystifying Scala Type System jeudi 29 novembre 12

description

Présentation au Paris Scala User Group le 28Nov 2012

Transcript of Demystifying Scala Type System

Page 1: Demystifying Scala Type System

David GalichetCTO @ CoachClub

Demystifying Scala Type System

jeudi 29 novembre 12

Page 2: Demystifying Scala Type System

Schedule

Scala Types 101

Types Variance and Type bounds

Abstract Type members

Ad-Hoc Polymorphism

Existential Types

Generalized Type Constraints

jeudi 29 novembre 12

Page 3: Demystifying Scala Type System

What is a type system ?

“A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of values they compute.“ – Benjamin Pierce

jeudi 29 novembre 12

Page 4: Demystifying Scala Type System

What is a type ?

A Type defines a set of values a variable can posses and a set of functions that can be applied to these values

Set of values can be defined as

Cartesian product Types (like case classes or Tuples)

Sum Types (like Either)

Types can be Abstract and/or Polymorph

jeudi 29 novembre 12

Page 5: Demystifying Scala Type System

What is a type ?

In Functional Languages like Scala, a Function is also a Type that can be assigned to a variable or (higher order) function or returned by a (higher order) function

jeudi 29 novembre 12

Page 6: Demystifying Scala Type System

Why typing ?“Make illegal states unrepresentable“ - Yaron Minsky

“Where static typing fits, do it every time because it has just fantastic maintenance benefits.” - Simon Peyton Jones

Compiler can use Type informations to optimize compiled code

jeudi 29 novembre 12

Page 7: Demystifying Scala Type System

Scala Types 101

Scala is Object Oriented and Functional

Scala has a strong and static Type SystemTypes are checked at compile time

Types can be inferred by the compiler

Functions are Types : A => B

jeudi 29 novembre 12

Page 8: Demystifying Scala Type System

Scala Types 101

Types are used to define

[abstract] classes

objects

traits

jeudi 29 novembre 12

Page 9: Demystifying Scala Type System

Scala Types 101Scala types hierarchy

Enclosed by : Top type ⟙ (Any)

Bottom type ⟘ (Nothing)

Java Primitive Types are wrapped under AnyVal (Unit, Long, Double, Boolean ...)

Since 2.10, you can define your own AnyVal

Any⟙

AnyRefAnyVal

Primitive Types wrappers All Types

Nothing ⟘

jeudi 29 novembre 12

Page 10: Demystifying Scala Type System

Scala Types 101

Scala Types can be parameterizedList[A]

Either[A, B]

Functions can also take type parameters

def show[A](a:A):String = a.toString

jeudi 29 novembre 12

Page 11: Demystifying Scala Type System

Type Variance and Bounds

Type Variance goal is to define inheritance relation

By default, Type Parameters are invariant

They can also be defined as co-variant or contra-variant

jeudi 29 novembre 12

Page 12: Demystifying Scala Type System

Type Variance and Bounds

Co-Variance(M[+T])

if A extends B then M[A] extends M[B]

Contra-Variance (M[-T])

if A extends B then M[B] extends M[A]

B

A

⇒M[B]

M[A]

B

A

⇒M[A]

M[B]

jeudi 29 novembre 12

Page 13: Demystifying Scala Type System

Type Variance and Bounds

Some examples of Types with varying type parametersList[+A]

Writer[-A]

Function1[-T, +R]

jeudi 29 novembre 12

Page 14: Demystifying Scala Type System

Type Variance and Bounds

scala> class Test[+A] { | def test(a: A): String = a.toString | }<console>:8: error: covariant type A occurs in contravariant position in type A of value a

WTF ?

jeudi 29 novembre 12

Page 15: Demystifying Scala Type System

Type Variance and BoundsFirst of all, take a look at Functions :

Function1[-T,+R]

Functions are Co-Variant on return type (+R) and Contra-Variant on parameters (-T) !

We can substitute Function1[A,D] by Function1[B,C] : B Function1[A, D]

C

D

A Function1[B, C]

⋀ ⇒

jeudi 29 novembre 12

Page 16: Demystifying Scala Type System

Type Variance and Bounds

Type A should be either Invariant or Contra-Variant but it’s Co-Variant

class Test[+A] { def test(a: A): String = a.toString}

This is a Function1 instance !

jeudi 29 novembre 12

Page 17: Demystifying Scala Type System

Type Variance and Bounds

Solution : introduce a bounded Type

class Test[+A] { def test[B >: A](b: B): String = b.toString}

Lower Type Bound : this new Type B is a super Type of A

Method test will accept A or any super Type of A

jeudi 29 novembre 12

Page 18: Demystifying Scala Type System

Type Variance and BoundsImplementation of a List

Inherit from any List[T]

trait List[+T] { def ::[U >: T](u: U): List[U] = Cons(u, this)}case class Cons[T](head: T, tail: List[T]) extends List[T]

case object Nil extends List[Nothing]

jeudi 29 novembre 12

Page 19: Demystifying Scala Type System

Type Variance and Bounds

Variance is not applicable to mutable state :

trait Mutable[+T] { var t: T // generate a setter:

// def t_=(t: T) {this.t = t}}

Co-Variant parameter in Contra-Variant position

⇒ A mutable List can’t be Co-Variant !

jeudi 29 novembre 12

Page 20: Demystifying Scala Type System

Type Variance and BoundsImplementation of a Writer - Part1

class B { def toString = "I’m B" }class A extends B { def toString = "I’m A" }

trait Writer[-T] { def write(t: T): String }

val bWriter = new Writer[B] { def write(b: B): String = b.toString }

def write[T](t: T)(w: Writer[T]) = w.write(t)

Inherit from any List[T]

jeudi 29 novembre 12

Page 21: Demystifying Scala Type System

Type Variance and BoundsImplementation of a Writer - Part2

B Write[A]

A Write[B]

write(new B)(bWriter)res> String = I’m B

write(new A)(bWriter)res> String = I’m A

We need a Writer[A]

Fortunately, Writer[B] extends Writer[A]:

jeudi 29 novembre 12

Page 22: Demystifying Scala Type System

Type memberConcrete Types can be defined in a class, trait or object

type Color = String // type Aliastype Valid[X] = Either[Throwable, X] // Valid is parametrized with X

We can define these types with their kind :

Color or String has kind *

Valid or Option has kind * ➞ *

Either has kind * ➞ * ➞ *

jeudi 29 novembre 12

Page 23: Demystifying Scala Type System

Abstract Type membersWe can define Abstract Type in abstract classes or traits

Abstract Types are another way to parameterize Typestrait Foodclass Grass extends Foodclass Fish extends Food

trait Species { type SuitableFood <: Food}

trait Animal extends Species

class Cow extends Animal { type SuitableFood = Grass}

jeudi 29 novembre 12

Page 24: Demystifying Scala Type System

Abstract Type membersThe parameterized type way :

trait Foodclass Grass extends Foodclass Fish extends Food

trait Species[T <: Food]trait Animal[T <: Food] extends Species[T]

class Cow extends Animal[Grass]

jeudi 29 novembre 12

Page 25: Demystifying Scala Type System

Ad-Hoc Polymorphism

Ad-Hoc polymorphism is a way to add behavior to an existing class without modifying it

In Haskell, polymorphism is achieved using typeclasses

abs :: (Num a, Ord a) => a -> aabs x = if x < 0 then -x else x

Typeclasses

jeudi 29 novembre 12

Page 26: Demystifying Scala Type System

Ad-Hoc Polymorphism

In Scala, we can achieve Ad-Hoc polymorphism using implicits

implicits are used in two places

implicit conversion to convert a type to another

implicit parameter

jeudi 29 novembre 12

Page 27: Demystifying Scala Type System

Ad-Hoc PolymorphismIn Scala, we can achieve Ad-Hoc polymorphism using implicits

Scala library defines many Typeclasses to achieve Ad-Hoc polymorphism : Integral, Numeric, Ordering ...

def abs[T](x: T)(implicit num: Numeric[T]): T = if(num.lt(x, num.zero)) num.negate(x) else x

def max[T: Ordering](x: T, y: T): T = implicitly[Ordering[T]].max(x, y)

jeudi 29 novembre 12

Page 28: Demystifying Scala Type System

Ad-Hoc PolymorphismWe can define our own instances of existing typeclasses

case class Student(name: String, score: Float)

implicit object StudentOrdering extends Ordering[Student] { def compare(x: Student, y: Student) = x.score.compareTo(y.score)}

scala> max(Student("Bob", 5.6F), Student("Alice", 5.8F))res0: Student = Student(Alice,5.8)

jeudi 29 novembre 12

Page 29: Demystifying Scala Type System

Ad-Hoc PolymorphismWe can define our own instances of typeclasses

implicit class Printable[A](a: A) { // since Scala 2.10 def printOut(): Unit = println(a.toString)}

scala> "test".printOuttest

jeudi 29 novembre 12

Page 30: Demystifying Scala Type System

Ad-Hoc PolymorphismA more concrete example - Part 1

trait Searchable[T] { val id: String val indexedContent: String }

class SearchEngine[T](defaultBuilder: String => T){ def index(searchable: Searchable[T]) { /* ... */ } def search(query: String)(builder: String => T = defaultBuilder): T = builder("0")}

jeudi 29 novembre 12

Page 31: Demystifying Scala Type System

Ad-Hoc Polymorphism

case class Person(id: Long, name: String)

implicit def person2Searchable(p: Person) = new Searchable[Person] { val id = p.id.toString val indexedContent = p.name }

val fakeEngine = new SearchEngine[Person]( id => Person(id.toLong, "retrieved content") )

A more concrete example - Part 2

jeudi 29 novembre 12

Page 32: Demystifying Scala Type System

Ad-Hoc PolymorphismPolymorphic Typeclasses instance definition

class Hour[X] private (val x: X) { /* ... */ }

object Hour { def apply[X](x: X)(implicit int: Integral[X]):Hour[X]= new Hour(int.rem(int.plus(int.rem(x, int.fromInt(12)), int.fromInt(12)), int.fromInt(12)))}

implicit def hour2Monoid[X](implicit int: Integral[X]): Monoid[Hour[X]] = new Monoid[Hour[X]] { def append(f1: Hour[X], f2: => Hour[X]) = Hour(int.rem(int.plus(f1.x, f2.x), int.fromInt(12))) def zero = Hour(int.zero)}

jeudi 29 novembre 12

Page 33: Demystifying Scala Type System

Existential Types

Existential types are reference to type parameter that is unknown

The Scala existential type in M[_] is the dual of Java wildcard M<?>

They can be defined using : M[T] forSome { type T }

or M[_]

jeudi 29 novembre 12

Page 34: Demystifying Scala Type System

Existential Types

We can bound existential types :M[T] forSome { type T <: AnyRef }

or M[_ <: AnyRef]

jeudi 29 novembre 12

Page 35: Demystifying Scala Type System

Generalized Type ConstraintsConstrain type using an implicit

=:= same type

<:< lower type

>:> super type

jeudi 29 novembre 12

Page 36: Demystifying Scala Type System

Generalized Type Constraints

trait Foodclass Grass extends Foodclass Fish extends Food

trait Animal[SuitableFood <: Food] { def fish(implicit ev: SuitableFood =:= Fish){ println("I'm fishing") }}

class Cow extends Animal[Grass]class Bear extends Animal[Fish]

Example :

jeudi 29 novembre 12