Type Systems
Jordan Parmer
@ProfParmer
What is a Type System?
“A set of rules that assigns a property called a ‘type’ to the
various constructs of a computer program such as variables,
expressions, functions, or modules.
These types formalize and enforce the otherwise implicit
categories the programmer users for algebraic data types,
data structures, or other components.”
-Wikipedia
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 in Types and Programming Languages
What is a Type System?
“Intuitively speaking, you want your type system to induce a
system of logic that is consistent, i.e. that doesn't allow false
theorems to be proven. The more inconsistent the system of
logic induced by the type system is, the less useful the type
system is as a proof of correctness of the code.”
- Jorg Mittag
What are the goals of a type
system?
Type System Goals
◦ Check for bad program behavior
◦ Early detection of program errors
◦ Enable abstractions
◦ Protect integrity of user defined abstractions
◦ Documentation
▫ Easy to reason code’s purpose
▫ Doesn’t drift like code comments
How do we evaluate programming
languages?
We Evaluate Languages By…
1. Domain
2. Community
3. Type System
The More You Use
Your Type System
The More It Works
For You
The More You Use
Your Type System
The More It Works
For You
If you just use
primitive types,
don’t expect much
more than primitive
behavior from your
type system.
Use rich types to
describe your
domain, and you’ll
find your type
system actually
enforcing your
domain!
Expectations of
This Talk
1. Only scratching the surface
2. Not going deep into type theory
3. Code examples biased towards Scala but
concepts apply to many languages
Comic by Cassandra Calin
Fundamental Type System
Classifications
Fundamental Type System Classifications
◦ Type Safety
◦ Static vs Dynamic Type Checking
◦ Type Inference
Type Safety
“A safe language is one that protects
its own abstractions.” - Pierce (TaPL)
Type Safety
Unsafe Behavior
● Dereferencing null pointer
● Accessing array out of
bounds
● Using uninitialized variables
● Casting between types
● Not checking parameter lists
Safe Behavior
● Runtime Exceptions
○ NullPointerException
○ ArrayIndexOutOfBoundsException
● Not allowing uninitialized
variables
● Compile time parameter list
agreement checks
Type Safety
Unsafe Behavior
● Dereferencing null pointer
● Accessing array out of
bounds
● Using uninitialized variables
● Casting between types
● Not checking parameter lists
Safe Behavior
● Runtime Exceptions
○ NullPointerException
○ ArrayIndexOutOfBoundsException
● Not allowing uninitialized
variables
● Compile time parameter list
agreement checks
Type safety is not binary. Languages vary in safety on a per
feature basis (e.g. memory safety, abstraction safety, runtime
safety, cast safety, etc.)
Static vs Dynamic
Static Type Checking
Verify safety of program before
execution.
Dynamic Type Checking
Verify safety of program at
execution.
Question: Why do people
get into religious
arguments over static vs
dynamic type checking?
Kidding! More seriously, what some
of the pros/cons of static vs dynamic
type systems?
Static vs Dynamic
Static Type Checking
◦ Early feedback
◦ More reliable
◦ More optimizable
◦ Longer edit-compile-test cycle
Dynamic Type Checking
◦ Shorter edit-compile-test cycle
◦ More flexible
◦ Good for prototyping
◦ Better at metaprogramming
◦ Get ready for lots and lots of unit testing
Static vs Dynamic: Language Examples
Static
◦ C
◦ C++
◦ Java
◦ C#
◦ Scala
◦ Haskell
◦ Rust
◦ Kotlin
◦ Go
Dynamic
◦ JavaScript
◦ Ruby
◦ Python
◦ Perl
◦ PHP
◦ Lisp
◦ Clojure
◦ R
◦ Bash
Dynamic Static
Weak
Strong
Erlang
Clojure
Python
Groovy
Ruby
Magik
Perl
VB
PHP
JavaScript
C#
F# Haskell
Scala
Java
C
C++
Type Inference
Automatic detection of data type
◦ Feature of some strongly statically typed
languages
◦ Especially prevalent in functional
programming languages
Explicitly Type Annotated Type Inference
val x: Int = 100
val y: Seq[String] = Seq(“apple”, “orange”)
val z: User = new User(“Jordan Parmer”)
val a: Option[String] = findKey(“type”)
val x = 100
val y = Seq(“apple”, “orange”)
val z = new User(“Jordan Parmer”)
val a = findKey(“type”)
Type Inference
Automatic detection of data type
◦ Feature of some strongly statically typed
languages
◦ Especially prevalent in functional
programming languages
Explicitly Type Annotated Type Inference
val x: Int = 100
val y: Seq[String] = Seq(“apple”, “orange”)
val z: User = new User(“Jordan Parmer”)
val a: Option[String] = findKey(“type”)
val x = 100
val y = Seq(“apple”, “orange”)
val z = new User(“Jordan Parmer”)
val a = findKey(“type”)
Still statically typed at
compile time
Strong type inference paves the way
for expressions over statements.
Type Driven Development
Focus on type signatures instead of implementation. With pure-
ish functional languages, if it compiles, you know it is likely
correct.
Lets you fill in the details later.
Rich Type System
Features
Functions as Types
Higher Order Functions
Higher Order Functions
Function that either takes another
function as an argument and/or
returns a function as its result.
Higher Order Functions
Function that either takes another
function as an argument and/or
returns a function as its result.
Higher Order Functions
Function that either takes another
function as an argument and/or
returns a function as its result.
Partial function
application via currying
Returns String => String
Question: So what is the problem with
conditionals anyway?
#ConditionalsAreEvil
Algebraic Data Types
Algebraic Data Types
A variant/union type that supports
sum and product algebras. Pattern
matching capabilities on type shape.
◦ Sum is alternation
A | B, meaning A or B but not both
◦ Product is combination
A B, meaning A and B together
Algebraic Data Types
A variant/union type that supports
sum and product algebras. Pattern
matching capabilities on type shape.
Product Types (A AND B)
◦ Tuples: (Int, Boolean)
◦ Classes: class Foo(x: Int, y: String)
Algebraic Data Types
A variant/union type that supports
sum and product algebras. Pattern
matching capabilities on type shape.
Sum Types (A OR B NOT BOTH)
◦ Option[T] (Some | None)
Maybe[T] (Just | Empty)
◦ Either[B, A] (Left | Right)
◦ Try[T] (Success | Failure)
Algebraic Data Types
Example ADT of Sum Type
Algebraic Data Types
Example ADT of Sum Type
If we don’t include a case for one of the members
of the ADT, we get a compiler warning. Called
exhaustive checking.
ADT’s of the sum algebra look like
‘enum’ types from C/C++ but those
languages internally treat them as
integers.
These are more sophisticated taking
custom type shapes.
Polymorphism
Higher abstractions with type safety
Polymorphism
Parametric Polymorphism
Subtype Polymorphism
Ad-Hoc Polymorphism (aka Type Classes)
Question: When people talk about
polymorphism, what kinds of
problems are they trying to solve?
Parametric Polymorphism
Same as generic types from C#,
Java. Types are parameterized.
Parametric Polymorphism
Parametric Polymorphism
Parametric Polymorphism
Parametric Polymorphism
Parametric Polymorphism
Parametric Polymorphism
Bounds Checking
◦ Upper bounds (supertype)
◦ Lower bounds (subtype)
Partial Orders (from Order Theory)
A partial order is a binary relation <:
over a set P.
When a <: b, we say that a is related
to b but does not imply b is related to
a.
Upper Bounds Checking
Lower Bounds Checking
Existential Types
When you need a parameterized type but you don’t care what
it is.
Existential Types
We need [T] so we can support any “List of type T”
but we don’t actually need to use ‘T’ in the
implementation.
Existential Types
We need [T] so we can support any “List of type T”
but we don’t actually need to use ‘T’ in the
implementation.
Type Variance
What if we want to model a factory
that creates vehicles?
Invariant
Invariant
Covariant
Covariant
Covariant
One more example:
What if we want to limit the models
the factory is able to produce?
Let’s say we can make vehicles as
long as the model isn’t too
specialized.
In other words, control the direction
of the specialization. “Up to this point
and no further”.
Contravariant
Contravariant
When is contravariant subtyping
actually practical?
Practical Contravariant Example
trait Function1[-P, +R] {
def apply(p: P): R
}
Function1[GrandParent, Child] <:
Function1[Parent, Parent]
Functor
A => B
Helpful Explanation: https://www.atlassian.com/blog/software-teams/covariance-and-contravariance-
in-scala
Inputs for A can only have fewer requirements.
Outputs for B must at least be as specialized as
B since caller expects all of B to be available.
Type Classes
Ad-hoc Polymorphism
What are disadvantages to OOP
inheritance?
Inheritance Disadvantages
◦ Violates encapsulation
◦ Layers and layers of state
◦ Difficult to follow hierarchy
◦ Re-use abuse (we bad at correct DRY)
◦ Can’t expand behavior if you don’t own
codebase (e.g. libraries)
What are common alternatives to
inheritance?
Composition over inheritance is one.
Another Way
To
Approach This?
How about adding behavior to types
we don’t own?
How about selectively applying
behaviors in different scopes of our
code base?
How about applying lawful behaviors
to abstractions?
Type classes are the answer
Separation of Data from Behavior
Separation of Data from Behavior
Could implement interfaces and traits…
...but what about cases where we don’t own the code base?
Elements of a type class
1. The type class itself
(desired behavior)
2. Instances of particular types
(implementation of behavior)
3. Interface or API exposed to users
Behavior
Implementation for
Integer
Implementation for
Boolean
Implementation for
User
Implementation for
Record
Implementation for
Json
Function Requiring
Type with Behavior
How Is This Different From Interfaces
In OOP?
Behavior
Implementation for
Integer
Implementation for
Boolean
Implementation for
User
Implementation for
Record
Implementation for
Json
Function Requiring
Type with Behavior
Compiler error if calling
function with type that
doesn’t implement
behavior.
Behavior
Implementation for
Integer
Implementation for
Boolean
Implementation for
User
Implementation for
Record
Implementation for
Json
Function Requiring
Type with Behavior
Implementations can be
imported into specific
scopes.
Behavior
Implementation for
Integer
Implementation for
Boolean
Implementation for
User
Implementation for
Record
Implementation for
Json
Function Requiring
Type with Behavior
Implementations can be
provided for types we don’t own
or have access to code.
Behavior
Implementation for
Integer
Implementation for
Boolean
Implementation for
User
Implementation for
Record
Implementation for
Json
Function Requiring
Type with Behavior
We can easily drop in new
implementations.
Implementation for
NewThing
With type classes we flip the thinking;
instead of types carrying behaviors,
we have templates of behaviors with
interpretations for different types.
1. The Type Class (Desired Behavior)
This is the actual behavior:
A => Json
Code provided by Noel Welsh and Dave Gurnell in book “Scala with Cats”
2. Type class instances (implementations of behavior)
Code provided by Noel Welsh and Dave Gurnell in book “Scala with Cats”
3. Type class access (api to caller)
The compiler finds our implicit
behavior in scope (because of the
localized import) and uses the type’s
implementation in the type class.
Code provided by Noel Welsh and Dave Gurnell in book “Scala with Cats”
Disadvantages?
● Verbose if language doesn’t have native support
○ Haskell has native support
○ Scala does not and requires implicit boilerplate code
● Paradigm shift from traditional OOP/interfaces
Examples
● Haskell
○ It’s type classes all the way down
● Scala
○ ScalaZ
○ Cats
Higher Kinded Types
...another talk...another day...
Thanks!
ANY QUESTIONS?
You can find me on Twitter/LinkedIn at
@ProfParmer
Jordan Parmer

Type Systems

  • 1.
  • 2.
    What is aType System? “A set of rules that assigns a property called a ‘type’ to the various constructs of a computer program such as variables, expressions, functions, or modules. These types formalize and enforce the otherwise implicit categories the programmer users for algebraic data types, data structures, or other components.” -Wikipedia
  • 3.
    What is aType 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 in Types and Programming Languages
  • 4.
    What is aType System? “Intuitively speaking, you want your type system to induce a system of logic that is consistent, i.e. that doesn't allow false theorems to be proven. The more inconsistent the system of logic induced by the type system is, the less useful the type system is as a proof of correctness of the code.” - Jorg Mittag
  • 5.
    What are thegoals of a type system?
  • 6.
    Type System Goals ◦Check for bad program behavior ◦ Early detection of program errors ◦ Enable abstractions ◦ Protect integrity of user defined abstractions ◦ Documentation ▫ Easy to reason code’s purpose ▫ Doesn’t drift like code comments
  • 7.
    How do weevaluate programming languages?
  • 8.
    We Evaluate LanguagesBy… 1. Domain 2. Community 3. Type System
  • 9.
    The More YouUse Your Type System The More It Works For You
  • 10.
    The More YouUse Your Type System The More It Works For You If you just use primitive types, don’t expect much more than primitive behavior from your type system. Use rich types to describe your domain, and you’ll find your type system actually enforcing your domain!
  • 11.
  • 12.
    1. Only scratchingthe surface 2. Not going deep into type theory 3. Code examples biased towards Scala but concepts apply to many languages Comic by Cassandra Calin
  • 13.
  • 14.
    Fundamental Type SystemClassifications ◦ Type Safety ◦ Static vs Dynamic Type Checking ◦ Type Inference
  • 15.
    Type Safety “A safelanguage is one that protects its own abstractions.” - Pierce (TaPL)
  • 16.
    Type Safety Unsafe Behavior ●Dereferencing null pointer ● Accessing array out of bounds ● Using uninitialized variables ● Casting between types ● Not checking parameter lists Safe Behavior ● Runtime Exceptions ○ NullPointerException ○ ArrayIndexOutOfBoundsException ● Not allowing uninitialized variables ● Compile time parameter list agreement checks
  • 17.
    Type Safety Unsafe Behavior ●Dereferencing null pointer ● Accessing array out of bounds ● Using uninitialized variables ● Casting between types ● Not checking parameter lists Safe Behavior ● Runtime Exceptions ○ NullPointerException ○ ArrayIndexOutOfBoundsException ● Not allowing uninitialized variables ● Compile time parameter list agreement checks Type safety is not binary. Languages vary in safety on a per feature basis (e.g. memory safety, abstraction safety, runtime safety, cast safety, etc.)
  • 18.
    Static vs Dynamic StaticType Checking Verify safety of program before execution. Dynamic Type Checking Verify safety of program at execution.
  • 19.
    Question: Why dopeople get into religious arguments over static vs dynamic type checking?
  • 20.
    Kidding! More seriously,what some of the pros/cons of static vs dynamic type systems?
  • 21.
    Static vs Dynamic StaticType Checking ◦ Early feedback ◦ More reliable ◦ More optimizable ◦ Longer edit-compile-test cycle Dynamic Type Checking ◦ Shorter edit-compile-test cycle ◦ More flexible ◦ Good for prototyping ◦ Better at metaprogramming ◦ Get ready for lots and lots of unit testing
  • 22.
    Static vs Dynamic:Language Examples Static ◦ C ◦ C++ ◦ Java ◦ C# ◦ Scala ◦ Haskell ◦ Rust ◦ Kotlin ◦ Go Dynamic ◦ JavaScript ◦ Ruby ◦ Python ◦ Perl ◦ PHP ◦ Lisp ◦ Clojure ◦ R ◦ Bash
  • 23.
  • 24.
    Type Inference Automatic detectionof data type ◦ Feature of some strongly statically typed languages ◦ Especially prevalent in functional programming languages Explicitly Type Annotated Type Inference val x: Int = 100 val y: Seq[String] = Seq(“apple”, “orange”) val z: User = new User(“Jordan Parmer”) val a: Option[String] = findKey(“type”) val x = 100 val y = Seq(“apple”, “orange”) val z = new User(“Jordan Parmer”) val a = findKey(“type”)
  • 25.
    Type Inference Automatic detectionof data type ◦ Feature of some strongly statically typed languages ◦ Especially prevalent in functional programming languages Explicitly Type Annotated Type Inference val x: Int = 100 val y: Seq[String] = Seq(“apple”, “orange”) val z: User = new User(“Jordan Parmer”) val a: Option[String] = findKey(“type”) val x = 100 val y = Seq(“apple”, “orange”) val z = new User(“Jordan Parmer”) val a = findKey(“type”) Still statically typed at compile time
  • 26.
    Strong type inferencepaves the way for expressions over statements.
  • 27.
    Type Driven Development Focuson type signatures instead of implementation. With pure- ish functional languages, if it compiles, you know it is likely correct. Lets you fill in the details later.
  • 28.
  • 29.
  • 30.
    Higher Order Functions Functionthat either takes another function as an argument and/or returns a function as its result.
  • 31.
    Higher Order Functions Functionthat either takes another function as an argument and/or returns a function as its result.
  • 32.
    Higher Order Functions Functionthat either takes another function as an argument and/or returns a function as its result. Partial function application via currying Returns String => String
  • 33.
    Question: So whatis the problem with conditionals anyway? #ConditionalsAreEvil
  • 34.
  • 35.
    Algebraic Data Types Avariant/union type that supports sum and product algebras. Pattern matching capabilities on type shape. ◦ Sum is alternation A | B, meaning A or B but not both ◦ Product is combination A B, meaning A and B together
  • 36.
    Algebraic Data Types Avariant/union type that supports sum and product algebras. Pattern matching capabilities on type shape. Product Types (A AND B) ◦ Tuples: (Int, Boolean) ◦ Classes: class Foo(x: Int, y: String)
  • 37.
    Algebraic Data Types Avariant/union type that supports sum and product algebras. Pattern matching capabilities on type shape. Sum Types (A OR B NOT BOTH) ◦ Option[T] (Some | None) Maybe[T] (Just | Empty) ◦ Either[B, A] (Left | Right) ◦ Try[T] (Success | Failure)
  • 38.
  • 39.
    Algebraic Data Types ExampleADT of Sum Type If we don’t include a case for one of the members of the ADT, we get a compiler warning. Called exhaustive checking.
  • 40.
    ADT’s of thesum algebra look like ‘enum’ types from C/C++ but those languages internally treat them as integers. These are more sophisticated taking custom type shapes.
  • 41.
  • 42.
  • 43.
    Question: When peopletalk about polymorphism, what kinds of problems are they trying to solve?
  • 44.
    Parametric Polymorphism Same asgeneric types from C#, Java. Types are parameterized.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
    Parametric Polymorphism Bounds Checking ◦Upper bounds (supertype) ◦ Lower bounds (subtype)
  • 51.
    Partial Orders (fromOrder Theory) A partial order is a binary relation <: over a set P. When a <: b, we say that a is related to b but does not imply b is related to a.
  • 52.
  • 53.
  • 54.
    Existential Types When youneed a parameterized type but you don’t care what it is.
  • 55.
    Existential Types We need[T] so we can support any “List of type T” but we don’t actually need to use ‘T’ in the implementation.
  • 56.
    Existential Types We need[T] so we can support any “List of type T” but we don’t actually need to use ‘T’ in the implementation.
  • 57.
  • 58.
    What if wewant to model a factory that creates vehicles?
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
    What if wewant to limit the models the factory is able to produce? Let’s say we can make vehicles as long as the model isn’t too specialized. In other words, control the direction of the specialization. “Up to this point and no further”.
  • 65.
  • 66.
  • 67.
    When is contravariantsubtyping actually practical?
  • 68.
    Practical Contravariant Example traitFunction1[-P, +R] { def apply(p: P): R } Function1[GrandParent, Child] <: Function1[Parent, Parent] Functor A => B Helpful Explanation: https://www.atlassian.com/blog/software-teams/covariance-and-contravariance- in-scala Inputs for A can only have fewer requirements. Outputs for B must at least be as specialized as B since caller expects all of B to be available.
  • 69.
  • 70.
    What are disadvantagesto OOP inheritance?
  • 71.
    Inheritance Disadvantages ◦ Violatesencapsulation ◦ Layers and layers of state ◦ Difficult to follow hierarchy ◦ Re-use abuse (we bad at correct DRY) ◦ Can’t expand behavior if you don’t own codebase (e.g. libraries)
  • 72.
    What are commonalternatives to inheritance?
  • 73.
  • 74.
  • 75.
    How about addingbehavior to types we don’t own?
  • 77.
    How about selectivelyapplying behaviors in different scopes of our code base?
  • 79.
    How about applyinglawful behaviors to abstractions?
  • 81.
    Type classes arethe answer
  • 82.
    Separation of Datafrom Behavior
  • 83.
    Separation of Datafrom Behavior Could implement interfaces and traits… ...but what about cases where we don’t own the code base?
  • 84.
    Elements of atype class 1. The type class itself (desired behavior) 2. Instances of particular types (implementation of behavior) 3. Interface or API exposed to users
  • 85.
    Behavior Implementation for Integer Implementation for Boolean Implementationfor User Implementation for Record Implementation for Json Function Requiring Type with Behavior
  • 86.
    How Is ThisDifferent From Interfaces In OOP?
  • 87.
    Behavior Implementation for Integer Implementation for Boolean Implementationfor User Implementation for Record Implementation for Json Function Requiring Type with Behavior Compiler error if calling function with type that doesn’t implement behavior.
  • 88.
    Behavior Implementation for Integer Implementation for Boolean Implementationfor User Implementation for Record Implementation for Json Function Requiring Type with Behavior Implementations can be imported into specific scopes.
  • 89.
    Behavior Implementation for Integer Implementation for Boolean Implementationfor User Implementation for Record Implementation for Json Function Requiring Type with Behavior Implementations can be provided for types we don’t own or have access to code.
  • 90.
    Behavior Implementation for Integer Implementation for Boolean Implementationfor User Implementation for Record Implementation for Json Function Requiring Type with Behavior We can easily drop in new implementations. Implementation for NewThing
  • 91.
    With type classeswe flip the thinking; instead of types carrying behaviors, we have templates of behaviors with interpretations for different types.
  • 92.
    1. The TypeClass (Desired Behavior) This is the actual behavior: A => Json Code provided by Noel Welsh and Dave Gurnell in book “Scala with Cats”
  • 93.
    2. Type classinstances (implementations of behavior) Code provided by Noel Welsh and Dave Gurnell in book “Scala with Cats”
  • 94.
    3. Type classaccess (api to caller) The compiler finds our implicit behavior in scope (because of the localized import) and uses the type’s implementation in the type class. Code provided by Noel Welsh and Dave Gurnell in book “Scala with Cats”
  • 95.
    Disadvantages? ● Verbose iflanguage doesn’t have native support ○ Haskell has native support ○ Scala does not and requires implicit boilerplate code ● Paradigm shift from traditional OOP/interfaces
  • 96.
    Examples ● Haskell ○ It’stype classes all the way down ● Scala ○ ScalaZ ○ Cats
  • 97.
    Higher Kinded Types ...anothertalk...another day...
  • 98.
    Thanks! ANY QUESTIONS? You canfind me on Twitter/LinkedIn at @ProfParmer Jordan Parmer