IAC 2024 - IA Fast Track to Search Focused AI Solutions
Demystifying Scala Type System
1. Demystifying Scala
Type System
David Galichet
CTO @ CoachClub
jeudi 29 novembre 12
2. Schedule
Scala Types 101
Types Variance and Type bounds
Abstract Type members
Ad-Hoc Polymorphism
Existential Types
Generalized Type Constraints
jeudi 29 novembre 12
3. 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
4. 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
5. 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
6. 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
7. Scala Types 101
Scala is Object Oriented and Functional
Scala has a strong and static Type System
Types are checked at compile time
Types can be inferred by the compiler
Functions are Types : A => B
jeudi 29 novembre 12
8. Scala Types 101
Types are used to define
[abstract] classes
objects
traits
jeudi 29 novembre 12
9. Scala Types 101 Any
⟙
Scala types hierarchy AnyVal AnyRef
Enclosed by : Primitive Types
All Types
wrappers
Top type ⟙ (Any)
Bottom type ⟘ (Nothing)
Nothing
⟘
Java Primitive Types are wrapped under AnyVal
(Unit, Long, Double, Boolean ...)
Since 2.10, you can define your own AnyVal
jeudi 29 novembre 12
10. Scala Types 101
Scala Types can be parameterized
List[A]
Either[A, B]
Functions can also take type parameters
def show[A](a:A):String = a.toString
jeudi 29 novembre 12
11. 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
12. Type Variance and Bounds
Co-Variance(M[+T]) B M[B]
if A extends B then M[A] extends M[B] A M[A]
Contra-Variance (M[-T])
if A extends B then M[B] extends M[A]
B M[A]
A M[B]
jeudi 29 novembre 12
13. Type Variance and Bounds
Some examples of Types with varying type parameters
List[+A]
Writer[-A]
Function1[-T, +R]
jeudi 29 novembre 12
14. 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
15. Type Variance and Bounds
First 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 D Function1[A, D]
⋀
A C Function1[B, C]
jeudi 29 novembre 12
16. Type Variance and Bounds
This is a Function1 instance !
class Test[+A] {
def test(a: A): String = a.toString
}
Type A should be either Invariant or Contra-Variant but it’s
Co-Variant
jeudi 29 novembre 12
17. 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
18. Type Variance and Bounds
Implementation of a List
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]
Inherit from any List[T]
jeudi 29 novembre 12
19. 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
20. Type Variance and Bounds
Implementation of a Writer - Part1
class B { def toString = "I’m B" }
class A extends B { def toString = "I’m A" }
Inherit from any List[T]
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)
jeudi 29 novembre 12
21. Type Variance and Bounds
Implementation of a Writer - Part2
write(new B)(bWriter)
res> String = I’m B We need a Writer[A]
write(new A)(bWriter)
res> String = I’m A
B Write[A]
Fortunately, Writer[B] extends Writer[A]:
A Write[B]
jeudi 29 novembre 12
22. Type member
Concrete Types can be defined in a class, trait or object
type Color = String // type Alias
type 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
23. Abstract Type members
We can define Abstract Type in abstract classes or traits
Abstract Types are another way to parameterize Types
trait Food
class Grass extends Food
class Fish extends Food
trait Species {
type SuitableFood <: Food
}
trait Animal extends Species
class Cow extends Animal {
type SuitableFood = Grass
}
jeudi 29 novembre 12
24. Abstract Type members
The parameterized type way :
trait Food
class Grass extends Food
class Fish extends Food
trait Species[T <: Food]
trait Animal[T <: Food] extends Species[T]
class Cow extends Animal[Grass]
jeudi 29 novembre 12
25. 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
Typeclasses
abs :: (Num a, Ord a) => a -> a
abs x = if x < 0 then -x else x
jeudi 29 novembre 12
26. 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
27. Ad-Hoc Polymorphism
In 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
28. Ad-Hoc Polymorphism
We 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
29. Ad-Hoc Polymorphism
We 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".printOut
test
jeudi 29 novembre 12
30. Ad-Hoc Polymorphism
A 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
31. Ad-Hoc Polymorphism
A more concrete example - Part 2
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") )
jeudi 29 novembre 12
33. 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
34. Existential Types
We can bound existential types :
M[T] forSome { type T <: AnyRef }
or M[_ <: AnyRef]
jeudi 29 novembre 12
35. Generalized Type Constraints
Constrain type using an implicit
=:= same type
<:< lower type
>:> super type
jeudi 29 novembre 12
36. Generalized Type Constraints
Example :
trait Food
class Grass extends Food
class 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]
jeudi 29 novembre 12