Liquid Types Manuel Eberl April 29, 2013 Prelude Type Systems - - PowerPoint PPT Presentation

liquid types
SMART_READER_LITE
LIVE PREVIEW

Liquid Types Manuel Eberl April 29, 2013 Prelude Type Systems - - PowerPoint PPT Presentation

Liquid Types Manuel Eberl April 29, 2013 Prelude Type Systems Prelude What is a type system? Prelude What is a type system? A way of classifying expressions by the kind of values they compute Prelude What is a type system? A way of


slide-1
SLIDE 1

Liquid Types

Manuel Eberl April 29, 2013

slide-2
SLIDE 2

Prelude – Type Systems

slide-3
SLIDE 3

Prelude

What is a type system?

slide-4
SLIDE 4

Prelude

What is a type system? A way of classifying expressions by the kind of values they compute

slide-5
SLIDE 5

Prelude

What is a type system? A way of classifying expressions by the kind of values they compute What are they for?

slide-6
SLIDE 6

Prelude

What is a type system? A way of classifying expressions by the kind of values they compute What are they for? To guarantee the absence of certain undesired or unintended behaviour

slide-7
SLIDE 7

Prelude

What is a type system? A way of classifying expressions by the kind of values they compute What are they for? To guarantee the absence of certain undesired or unintended behaviour What kinds of type systems are there?

slide-8
SLIDE 8

Prelude

What is a type system? A way of classifying expressions by the kind of values they compute What are they for? To guarantee the absence of certain undesired or unintended behaviour What kinds of type systems are there?

  • Well. . .
slide-9
SLIDE 9

Dynamic typing

Only one type: Any Give no information or guarantees whatsoever Used by: Python, JavaScript, PHP, . . .

slide-10
SLIDE 10

Dynamic typing

This is a very bad idea. Why? Enter Python:

a = "foo" b = 42 print(b - a)

slide-11
SLIDE 11

Dynamic typing

This is a very bad idea. Why? Enter Python:

a = "foo" b = 42 print(b - a)

Compiles without problems, but at runtime:

TypeError: unsupported operand type(s) for -: ’int’ and ’str’

Leads to unnecessary errors and/or erratic behaviour

slide-12
SLIDE 12

Dynamic typing

This is a very bad idea. Why? Enter Python:

a = "foo" b = 42 print(b - a)

Compiles without problems, but at runtime:

TypeError: unsupported operand type(s) for -: ’int’ and ’str’

Leads to unnecessary errors and/or erratic behaviour

(cf. Bernhardt, 2012: “Wat”, http://youtu.be/kXEgk1Hdze0)

slide-13
SLIDE 13

Static typing

Different types that correspond to “sorts” of values (e.g. Integer, String, Boolean) Guarantees the absence of type errors Used by: Java, C, Pascal

slide-14
SLIDE 14

Static typing

The same thing in Java:

String a = "foo"; int b = 42 ; System.out.println(b - a);

slide-15
SLIDE 15

Static typing

The same thing in Java:

String a = "foo"; int b = 42 ; System.out.println(b - a);

Compile time (!) error tells us something is wrong:

error: bad operand types for binary operator ’-’ System.out.println(b - a); ˆ first type: int second type: String

But: we have to annotate types (“String” resp. “int”)

slide-16
SLIDE 16

Static typing, but fancy

One nice addition: type inference Same guarantees, but less work Used by: Standard ML, OCaml, Haskell, . . . Down side: none!

slide-17
SLIDE 17

Type inference

Type inference: compiler figures out types (mostly) without

  • annotations. Same as before, now in Scala:

val a = "foo" val b = 42 System.out.println(b - a)

slide-18
SLIDE 18

Type inference

Type inference: compiler figures out types (mostly) without

  • annotations. Same as before, now in Scala:

val a = "foo" val b = 42 System.out.println(b - a)

Again: compile time error:

error: overloaded method value - with alternatives: (x: Double)Double <and> (x: Float)Float <and> ... cannot be applied to (java.lang.String) System.out.println(b - a); ˆ

Both the safety of static typing and the convenience of dynamic typing!

slide-19
SLIDE 19

Dependent types

So we can prevent errors caused by values being of the wrong “sort”, i.e. string instead of number.

slide-20
SLIDE 20

Dependent types

So we can prevent errors caused by values being of the wrong “sort”, i.e. string instead of number. But what about errors that are caused by restrictions on the actual values? dereferencing a null pointer array bounds violation division by zero Can we express restrictions on values in a type system as well?

slide-21
SLIDE 21

Dependent types

Example 1: Integer division Takes two integers, returns a rational number: (/) :: Int → Int → Rational

slide-22
SLIDE 22

Dependent types

Example 1: Integer division Takes two integers, returns a rational number: (/) :: Int → Int → Rational But the second operand must not be 0. So what we want is: (/) :: Int → {ν : Int | ν = 0} → Rational

slide-23
SLIDE 23

Dependent types

Example 2: List concatenation Take two lists, return the concatenated list: (++) :: List a → List a → List a But we lose some interesting information, e.g. about the result list’s length.

slide-24
SLIDE 24

Dependent types

Example 2: List concatenation Take two lists, return the concatenated list: (++) :: List a → List a → List a But we lose some interesting information, e.g. about the result list’s length. What we want is something like: (++) :: List a m → List a n → List a (m + n)

slide-25
SLIDE 25

Dependent types

Types can have arbitrary restrictions and depend on values Guarantees of arbitrary complexity Used by: Dependent ML, Idris Down side: type checking/inference undecidable, may require user-supplied proofs = ⇒ A lot of work!

slide-26
SLIDE 26

Liquid Types

Compromise: Liquid Types Restrict power of dependent types to decidable fragment = ⇒ inference of expressive types without user interaction

slide-27
SLIDE 27

Liquid Types

slide-28
SLIDE 28

Definition of Liquid Types

Basic idea: Take normal types as inferred by Hindley/Milner

slide-29
SLIDE 29

Definition of Liquid Types

Basic idea: Take normal types as inferred by Hindley/Milner Augment them with a specific kind of conditions (Refinement Types), i.e. linear constraints such as {ν : Int | ν > 0} or k : Int → {ν : Int | ν ≤ k}

slide-30
SLIDE 30

Definition of Liquid Types

Basic idea: Take normal types as inferred by Hindley/Milner Augment them with a specific kind of conditions (Refinement Types), i.e. linear constraints such as {ν : Int | ν > 0} or k : Int → {ν : Int | ν ≤ k} Allowed conditions should be powerful enough to say something interesting, but weak enough to allow automatic type inference

slide-31
SLIDE 31

Definition of Liquid Types

Basic idea: Take normal types as inferred by Hindley/Milner Augment them with a specific kind of conditions (Refinement Types), i.e. linear constraints such as {ν : Int | ν > 0} or k : Int → {ν : Int | ν ≤ k} Allowed conditions should be powerful enough to say something interesting, but weak enough to allow automatic type inference Not all programmes that are of type T can be recognised as such by type checking/inference

slide-32
SLIDE 32

Definition of Liquid Types

Example for the rest of the talk: simple equality/inequality constraints Conditions are conjunctions of qualifiers from e.g.: Q = {0 ≤ ν, ν = ∗, ∗ ≤ ν}

slide-33
SLIDE 33

Definition of Liquid Types

Example for the rest of the talk: simple equality/inequality constraints Conditions are conjunctions of qualifiers from e.g.: Q = {0 ≤ ν, ν = ∗, ∗ ≤ ν} Example: array get:: a : Array v → {ν : Int | 0 ≤ ν ∧ ν < len(a)} → v = ⇒ Compile-time guarantee: no array-bounds violations = ⇒ Compiler can drop bounds checks

slide-34
SLIDE 34

Liquid Type Inference

1 Run Hindley-Milner to obtain liquid type template 2 Use syntax-directed rules to generate system of constraints 3 Solve constraints using theorem prover

slide-35
SLIDE 35

Liquid Type Inference – Hindley-Milner

Hindley-Milner: standard type inference algorithm for functional languages Example:

We want to type: max (a :: Int) (b :: Int) = if a < b then b else a

slide-36
SLIDE 36

Liquid Type Inference – Hindley-Milner

Hindley-Milner: standard type inference algorithm for functional languages Example:

We want to type: max (a :: Int) (b :: Int) = if a < b then b else a We reason: the parameters a and b are of type Int.

slide-37
SLIDE 37

Liquid Type Inference – Hindley-Milner

Hindley-Milner: standard type inference algorithm for functional languages Example:

We want to type: max (a :: Int) (b :: Int) = if a < b then b else a We reason: the parameters a and b are of type Int. a < b is condition in an if, thus a < b :: Bool – okay

slide-38
SLIDE 38

Liquid Type Inference – Hindley-Milner

Hindley-Milner: standard type inference algorithm for functional languages Example:

We want to type: max (a :: Int) (b :: Int) = if a < b then b else a We reason: the parameters a and b are of type Int. a < b is condition in an if, thus a < b :: Bool – okay the if expression returns a or b, thus a and b have the same type as the result

slide-39
SLIDE 39

Liquid Type Inference – Hindley-Milner

Hindley-Milner: standard type inference algorithm for functional languages Example:

We want to type: max (a :: Int) (b :: Int) = if a < b then b else a We reason: the parameters a and b are of type Int. a < b is condition in an if, thus a < b :: Bool – okay the if expression returns a or b, thus a and b have the same type as the result therefore, the most precise result type is Int.

slide-40
SLIDE 40

Liquid Type Inference – Hindley-Milner

Hindley-Milner: standard type inference algorithm for functional languages Example:

We want to type: max (a :: Int) (b :: Int) = if a < b then b else a We reason: the parameters a and b are of type Int. a < b is condition in an if, thus a < b :: Bool – okay the if expression returns a or b, thus a and b have the same type as the result therefore, the most precise result type is Int. max :: Int → Int → Int

slide-41
SLIDE 41

Liquid Type Inference – Hindley-Milner

Example:

More formally: type derivation tree: Context Γ = [a : Int; b : Int] Γ(a) = Int Γ ⊢ a : Int Γ(b) = Int Γ ⊢ b : Int Γ ⊢ a < b : Bool Γ(b) = Int Γ ⊢ b : Int Γ(a) = Int Γ ⊢ a : Int Γ ⊢ if a < b then b else a : Int

slide-42
SLIDE 42

Liquid Type Inference – Hindley-Milner

So Hindley-Milner can give us “normal” types for expressions. How do we get liquid types out of that?

slide-43
SLIDE 43

Liquid Type Inference – Hindley-Milner

So Hindley-Milner can give us “normal” types for expressions. How do we get liquid types out of that? = ⇒ introduce liquid type variable κ for each “base type” = ⇒ liquid type template Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a

slide-44
SLIDE 44

Liquid Type Inference – Hindley-Milner

So Hindley-Milner can give us “normal” types for expressions. How do we get liquid types out of that? = ⇒ introduce liquid type variable κ for each “base type” = ⇒ liquid type template Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a HM type: a : Int → b : Int → Int

slide-45
SLIDE 45

Liquid Type Inference – Hindley-Milner

So Hindley-Milner can give us “normal” types for expressions. How do we get liquid types out of that? = ⇒ introduce liquid type variable κ for each “base type” = ⇒ liquid type template Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a HM type: a : Int → b : Int → Int Liquid type template: a : {ν : Int | κa} → b : {ν : Int | κb} → {ν : Int | κr}

slide-46
SLIDE 46

Liquid Type Inference – Constraint generation

Next: what constraints are there on the κ? First, an example.

slide-47
SLIDE 47

Liquid Type Inference – Constraint generation

Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a Liquid type template: a : {ν : Int | κa} → b : {ν : Int | κb} → {ν : Int | κr}

slide-48
SLIDE 48

Liquid Type Inference – Constraint generation

Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a Liquid type template: a : {ν : Int | κa} → b : {ν : Int | κb} → {ν : Int | κr} Intuitively: We know that a :: κa and b :: κb. Let’s call these facts Γ.

slide-49
SLIDE 49

Liquid Type Inference – Constraint generation

Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a Liquid type template: a : {ν : Int | κa} → b : {ν : Int | κb} → {ν : Int | κr} Intuitively: We know that a :: κa and b :: κb. Let’s call these facts Γ. if a < b, we return b, therefore Γ ∧ a < b must imply κr

slide-50
SLIDE 50

Liquid Type Inference – Constraint generation

Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a Liquid type template: a : {ν : Int | κa} → b : {ν : Int | κb} → {ν : Int | κr} Intuitively: We know that a :: κa and b :: κb. Let’s call these facts Γ. if a < b, we return b, therefore Γ ∧ a < b must imply κr if b ≤ a, we return a, therefore Γ ∧ b ≤ a must imply κr

slide-51
SLIDE 51

Liquid Type Inference – Constraint generation

Example:

Programme: max (a :: Int) (b :: Int) = if a < b then b else a Liquid type template: a : {ν : Int | κa} → b : {ν : Int | κb} → {ν : Int | κr} Intuitively: We know that a :: κa and b :: κb. Let’s call these facts Γ. if a < b, we return b, therefore Γ ∧ a < b must imply κr if b ≤ a, we return a, therefore Γ ∧ b ≤ a must imply κr these are (morally) our two constraints

slide-52
SLIDE 52

Liquid Type Inference – Constraint generation

How is this done formally? Constraints are inferred with a system of syntax-directed rules: Γ ⊢Q v : Bool Γ; c ⊢Q e1 : ˆ σ Γ; ¬c ⊢Q e2 : ˆ σ

LT-IF

Γ ⊢Q if c then e1 else e2 : ˆ σ

slide-53
SLIDE 53

Liquid Type Inference – Constraint generation

How is this done formally? Constraints are inferred with a system of syntax-directed rules: Γ ⊢Q v : Bool Γ; c ⊢Q e1 : ˆ σ Γ; ¬c ⊢Q e2 : ˆ σ

LT-IF

Γ ⊢Q if c then e1 else e2 : ˆ σ Γ ⊢Q e : σ1 Γ ⊢ σ1 <: σ2

LT-SUB

Γ ⊢Q e : σ2

slide-54
SLIDE 54

Liquid Type Inference – Constraint generation

How is this done formally? Constraints are inferred with a system of syntax-directed rules: Γ ⊢Q v : Bool Γ; c ⊢Q e1 : ˆ σ Γ; ¬c ⊢Q e2 : ˆ σ

LT-IF

Γ ⊢Q if c then e1 else e2 : ˆ σ Γ ⊢Q e : σ1 Γ ⊢ σ1 <: σ2

LT-SUB

Γ ⊢Q e : σ2 Γ ϕ1 ⇒ ϕ2

<:-BASE

Γ ⊢ {ν : t | ϕ1} <: {ν : t | ϕ2}

slide-55
SLIDE 55

Liquid Type Inference – Constraint generation

Context: Γ = [a : {ν : Int | κa}; b : {ν : Int | κb}] Apply LT-IF rule: Γ ⊢Q a < b : Bool Γ; a < b ⊢Q b : κr Γ; b ≤ a ⊢Q a : κr Γ ⊢Q if a < b then b else a : κr

slide-56
SLIDE 56

Liquid Type Inference – Constraint generation

Context: Γ = [a : {ν : Int | κa}; b : {ν : Int | κb}] Apply LT-IF rule: Γ ⊢Q a < b : Bool Γ; a < b ⊢Q b : κr Γ; b ≤ a ⊢Q a : κr Γ ⊢Q if a < b then b else a : κr Apply subtyping rules LT-SUB and <:-BASE to prove the last two obligations: Γ; a < b ⊢Q b : {ν : Int | ν = b} Γ; a < b ν = b ⇒ κr Γ; a < b ⊢ {ν : Int | ν = b} <: {ν : Int | κr} Γ; a < b ⊢Q b : {ν : Int | κr}

slide-57
SLIDE 57

Liquid Type Inference – Constraint generation

Context: Γ = [a : {ν : Int | κa}; b : {ν : Int | κb}] Apply LT-IF rule: Γ ⊢Q a < b : Bool Γ; a < b ⊢Q b : κr Γ; b ≤ a ⊢Q a : κr Γ ⊢Q if a < b then b else a : κr Apply subtyping rules LT-SUB and <:-BASE to prove the last two obligations: Γ; a < b ⊢Q b : {ν : Int | ν = b} Γ; a < b ν = b ⇒ κr Γ; a < b ⊢ {ν : Int | ν = b} <: {ν : Int | κr} Γ; a < b ⊢Q b : {ν : Int | κr} Γ; b ≤ a ⊢Q a : {ν : Int | ν = a} Γ; b ≤ a ν = a ⇒ κr Γ; b ≤ a ⊢ {ν : Int | ν = a} <: {ν : Int | κr} Γ; b ≤ a ⊢Q a : {ν : Int | κr}

slide-58
SLIDE 58

Liquid Type Inference – Constraint generation

So we have the constraints: [a : κa; b : κb; a < b] ⊢ {ν : Int | ν = b} <: {ν : Int | κr} [a : κa; b : κb; b ≤ a] ⊢ {ν : Int | ν = a} <: {ν : Int | κr}

slide-59
SLIDE 59

Liquid Type Inference – Constraint generation

So we have the constraints: [a : κa; b : κb; a < b] ⊢ {ν : Int | ν = b} <: {ν : Int | κr} [a : κa; b : κb; b ≤ a] ⊢ {ν : Int | ν = a} <: {ν : Int | κr} What do they mean? Think of the κ as predicates. Then: κa(a) ∧ κb(b) ∧ a < b ∧ ν = b = ⇒ κr(ν) κa(a) ∧ κb(b) ∧ b ≤ a ∧ ν = a = ⇒ κr(ν) What are the strongest κ that satisfies these?

slide-60
SLIDE 60

Liquid Type Inference – Constraint solving

We have: a system of constraints on the κ We want: the strongest solution of the κ

slide-61
SLIDE 61

Liquid Type Inference – Constraint solving

We have: a system of constraints on the κ We want: the strongest solution of the κ Idea: each κ is a conjunction of finitely many qualifiers like 0 ≤ ν, ν ≤ x, . . .

slide-62
SLIDE 62

Liquid Type Inference – Constraint solving

We have: a system of constraints on the κ We want: the strongest solution of the κ Idea: each κ is a conjunction of finitely many qualifiers like 0 ≤ ν, ν ≤ x, . . . thus only finitely many assignments for the κ exist

slide-63
SLIDE 63

Liquid Type Inference – Constraint solving

We have: a system of constraints on the κ We want: the strongest solution of the κ Idea: each κ is a conjunction of finitely many qualifiers like 0 ≤ ν, ν ≤ x, . . . thus only finitely many assignments for the κ exist the theorem prover can tell us if an assignment is OK

slide-64
SLIDE 64

Liquid Type Inference – Constraint solving

We have: a system of constraints on the κ We want: the strongest solution of the κ Idea: each κ is a conjunction of finitely many qualifiers like 0 ≤ ν, ν ≤ x, . . . thus only finitely many assignments for the κ exist the theorem prover can tell us if an assignment is OK we can brute force all of them and pick the strongest one that is OK

slide-65
SLIDE 65

Liquid Type Inference – Constraint solving

Example:

Same function as before: Liquid type variables: κa, κb, κr Set κa, κb to True (we want to be able to call max with any values) Constraints: a < b ∧ ν = b = ⇒ κr(ν) b ≤ a ∧ ν = a = ⇒ κr(ν)

slide-66
SLIDE 66

Liquid Type Inference – Constraint solving

Example:

Same function as before: Liquid type variables: κa, κb, κr Set κa, κb to True (we want to be able to call max with any values) Constraints: a < b ∧ ν = b = ⇒ κr(ν) b ≤ a ∧ ν = a = ⇒ κr(ν) We try the assignment: κr → a ≤ ν ∧ b ≤ ν ∧ 0 ≤ ν Theorem prover says: No, because e.g. a = −2, b = −1 violates 0 ≤ ν

slide-67
SLIDE 67

Liquid Type Inference – Constraint solving

Example:

Same function as before: Liquid type variables: κa, κb, κr Set κa, κb to True (we want to be able to call max with any values) Constraints: a < b ∧ ν = b = ⇒ κr(ν) b ≤ a ∧ ν = a = ⇒ κr(ν) We try the assignment: κr → a ≤ ν ∧ b ≤ ν ∧ 0 ≤ ν Theorem prover says: No, because e.g. a = −2, b = −1 violates 0 ≤ ν We try the assignment: κr → a ≤ ν ∧ b ≤ ν Theorem prover says: Yes!

slide-68
SLIDE 68

Liquid Type Inference – Constraint solving

Idea: qualifiers are independent from one another: A ⇒ B ∧ C iff A ⇒ B and A ⇒ C so we can look at all the qualifiers separately

slide-69
SLIDE 69

Liquid Type Inference – Constraint solving

Idea: qualifiers are independent from one another: A ⇒ B ∧ C iff A ⇒ B and A ⇒ C so we can look at all the qualifiers separately Optimised algorithm: iterative weakening start with strongest possible assignment (all qualifiers) while there are unsatisfied constraints: weaken the κ involved as much as necessary in the end, we get the strongest valid liquid type (or an error)

slide-70
SLIDE 70

In practice

Typechecking takes very long

slide-71
SLIDE 71

In practice

Typechecking takes very long Implementations exist in multiple languages, mostly functional languages

slide-72
SLIDE 72

In practice

Typechecking takes very long Implementations exist in multiple languages, mostly functional languages But there are approaches for imperative languages as well

slide-73
SLIDE 73

In practice

Typechecking takes very long Implementations exist in multiple languages, mostly functional languages But there are approaches for imperative languages as well Original implementation of liquid types found a bug in OCaml bit vector library

slide-74
SLIDE 74

In practice

Typechecking takes very long Implementations exist in multiple languages, mostly functional languages But there are approaches for imperative languages as well Original implementation of liquid types found a bug in OCaml bit vector library Liquid Haskell: http://goto.ucsd.edu/˜rjhala/liquid/