Polymorphism, Subtyping, and Type Inference in MLsub Stephen Dolan - - PowerPoint PPT Presentation

polymorphism subtyping and type inference in mlsub
SMART_READER_LITE
LIVE PREVIEW

Polymorphism, Subtyping, and Type Inference in MLsub Stephen Dolan - - PowerPoint PPT Presentation

Polymorphism, Subtyping, and Type Inference in MLsub Stephen Dolan and Alan Mycroft Computer Laboratory University of Cambridge November 8, 2016 The select function select p v d = if ( p v ) then v else d In ML, select has type scheme . (


slide-1
SLIDE 1

Polymorphism, Subtyping, and Type Inference in MLsub

Stephen Dolan and Alan Mycroft

Computer Laboratory University of Cambridge

November 8, 2016

slide-2
SLIDE 2

The select function

select p v d = if (p v) then v else d In ML, select has type scheme ∀α. (α → bool) → α → α → α

slide-3
SLIDE 3

Data flow in select

select p v d = if (p v) then v else d v argument to p result d

slide-4
SLIDE 4

Data flow in select

select p v d = if (p v) then v else d v argument to p result d In MLsub, select has this type scheme: ∀α, β. (α → bool) → α → β → (α ⊔ β)

slide-5
SLIDE 5

Γ ⊢ e : τ

slide-6
SLIDE 6

Γ ⊢ e : τ

slide-7
SLIDE 7

Expressions of MLsub

We have functions x λx.e e1 e2 ... and records {ℓ1 = e1, . . . , ℓn = en} e.ℓ ... and booleans true false if e1 then e2 else e3 ... and let ˆ x let ˆ x = e1 in e2

slide-8
SLIDE 8

Γ ⊢ e : τ

slide-9
SLIDE 9

Typing rules of MLsub

MLsub is ML + (Sub) Γ ⊢ e : τ1 Γ ⊢ e : τ2 τ1 ≤ τ2

slide-10
SLIDE 10

Γ ⊢ e : τ

slide-11
SLIDE 11

Constructing Types

The standard definition of types looks like: τ ::= ⊥ | τ → τ | ⊤ (ignoring records and booleans for now)

slide-12
SLIDE 12

Constructing Types

The standard definition of types looks like: τ ::= ⊥ | τ → τ | ⊤ (ignoring records and booleans for now) with a subtyping relation like: ⊥ ≤ τ τ ≤ ⊤ τ ′

1 ≤ τ1

τ2 ≤ τ ′

2

τ1 → τ2 ≤ τ ′

1 → τ ′ 2

slide-13
SLIDE 13

Lattices

These types form a lattice:

◮ least upper bounds τ1 ⊔ τ2 ◮ greatest lower bounds τ1 ⊓ τ2

slide-14
SLIDE 14

Lattices

These types form a lattice:

◮ least upper bounds τ1 ⊔ τ2 ◮ greatest lower bounds τ1 ⊓ τ2

e1 : τ1 e2 : τ2 if rand () then e1 else e2 : τ 1 ⊔ τ 2

slide-15
SLIDE 15

Bizzarely difficult questions

Is this true, for all α? α → α ≤ ⊥ → ⊤

slide-16
SLIDE 16

Bizzarely difficult questions

Is this true, for all α? α → α ≤ ⊥ → ⊤ How about this? (⊥ → ⊤) → ⊥ ≤ (α → ⊥) ⊔ α

slide-17
SLIDE 17

Bizzarely difficult questions

Is this true, for all α? α → α ≤ ⊥ → ⊤ How about this? (⊥ → ⊤) → ⊥ ≤ (α → ⊥) ⊔ α Yes, it turns out, by case analysis on α.

slide-18
SLIDE 18

Bizzarely difficult questions

Is this true, for all α? α → α ≤ ⊥ → ⊤ How about this? (⊥ → ⊤) → ⊥ ≤ (α → ⊥) ⊔ α Yes, it turns out, by case analysis on α. And only by case analysis.

slide-19
SLIDE 19

Extensibility

Let’s add a new type of function τ1

  • → τ2.
slide-20
SLIDE 20

Extensibility

Let’s add a new type of function τ1

  • → τ2.

It’s a supertype of τ1 → τ2 “function that may have side effects”

slide-21
SLIDE 21

Extensibility

Let’s add a new type of function τ1

  • → τ2.

It’s a supertype of τ1 → τ2 “function that may have side effects” Now we have a counterexample: α = (⊤

  • → ⊥)
  • → ⊥
slide-22
SLIDE 22

Extensible type systems

Two techniques give us an extensible system:

◮ Add explicit type variables as indeterminates

gets rid of case analysis

slide-23
SLIDE 23

Extensible type systems

Two techniques give us an extensible system:

◮ Add explicit type variables as indeterminates

gets rid of case analysis

◮ Require a distributive lattice

gets rid of vacuous reasoning

slide-24
SLIDE 24

Combining types

How to combine different types into a single system? τ ::= bool | τ1 → τ2 | {ℓ1 : τ1, . . . , ℓn : τn}

slide-25
SLIDE 25

Combining types

How to combine different types into a single system? τ ::= bool | τ1 → τ2 | {ℓ1 : τ1, . . . , ℓn : τn} We should read ‘|’ as coproduct

slide-26
SLIDE 26

Concrete syntax

Build an actual syntax for types, by writing down all the operations on types: τ ::= bool | τ1 → τ2 | {ℓ1 : τ1, . . . , ℓn : τn} | α | ⊤ | ⊥ | τ ⊔ τ | τ ⊓ τ

slide-27
SLIDE 27

Concrete syntax

Build an actual syntax for types, by writing down all the operations on types: τ ::= bool | τ1 → τ2 | {ℓ1 : τ1, . . . , ℓn : τn} | α | ⊤ | ⊥ | τ ⊔ τ | τ ⊓ τ then quotient by the equations of distributive lattices, and the subtyping order.

slide-28
SLIDE 28

Resulting types

We end up with all the standard types

slide-29
SLIDE 29

Resulting types

We end up with all the standard types ... with the same subtyping order

slide-30
SLIDE 30

Resulting types

We end up with all the standard types ... with the same subtyping order ... but we identify fewer of the weird types {foo : bool} ⊓ (⊤ → ⊤) ≤ bool

slide-31
SLIDE 31

Γ ⊢ e : τ

slide-32
SLIDE 32

Principality in ML

Intuitively, For any e typeable under Γ, there’s a best type τ

slide-33
SLIDE 33

Principality in ML

Intuitively, For any e typeable under Γ, there’s a best type τ but it’s a bit more complicated than that: For any e typeable under Γ, there’s a τ and a substitution σ such that every possible typing of e under Γ is a substitution instance of σΓ, τ.

slide-34
SLIDE 34

Reformulating the typing rules

The complexity arises because Γ is part question, part answer.

slide-35
SLIDE 35

Reformulating the typing rules

The complexity arises because Γ is part question, part answer. Instead, split Γ:

◮ ∆ maps λ-bound x to a type τ ◮ Π maps let-bound ˆ

x to a typing schemes [∆]τ

slide-36
SLIDE 36

Π e : [∆]τ

slide-37
SLIDE 37

question

Π e :

answer

  • [∆]τ
slide-38
SLIDE 38

Subsumption

Define ≤∀ as the least relation closed under:

◮ Instatiation, replacing type variables with types ◮ Subtyping, replacing types with supertypes

slide-39
SLIDE 39

Principality in MLsub

A principal typing scheme for e under Π is a [∆]τ that subsumes any other.

slide-40
SLIDE 40

The choose function

choose takes two values and returns one of them: choose : ∀α.α1 → α2 → α3

slide-41
SLIDE 41

The choose function

choose takes two values and returns one of them: choose : ∀α.α1 → α2 → α3 In ML, α1 = α2 = α3. With subtyping, α1 ≤ α3, α2 ≤ α3, but α1 and α2 may be incomparable.

slide-42
SLIDE 42

The choose function

choose takes two values and returns one of them: choose : ∀α.α1 → α2 → α3 In ML, α1 = α2 = α3. With subtyping, α1 ≤ α3, α2 ≤ α3, but α1 and α2 may be incomparable. choose : ∀αβ.α → β → α ⊔ β

slide-43
SLIDE 43

The choose function

choose takes two values and returns one of them: choose : ∀α.α1 → α2 → α3 In ML, α1 = α2 = α3. With subtyping, α1 ≤ α3, α2 ≤ α3, but α1 and α2 may be incomparable. choose : ∀αβ.α → β → α ⊔ β These are equivalent (≡∀): subsume each other

slide-44
SLIDE 44

Input and output types

τ ⊔ τ ′: produces a value which is a τ or a τ ′ τ ⊓ τ ′: requires a value which is a τ and a τ ′ ⊔ is for outputs, and ⊓ is for inputs.

slide-45
SLIDE 45

Input and output types

τ ⊔ τ ′: produces a value which is a τ or a τ ′ τ ⊓ τ ′: requires a value which is a τ and a τ ′ ⊔ is for outputs, and ⊓ is for inputs. Divide types into

◮ output types τ + ◮ input types τ −

slide-46
SLIDE 46

Polar types

τ + ::= bool | τ −

1 → τ + 2 | {ℓ1 : τ + 1 , . . . , ℓn : τ + n } |

α | τ +

1 ⊔ τ + 2 | ⊥ | µα.τ +

τ − ::= bool | τ +

1 → τ − 2 | {ℓ1 : τ − 1 , . . . , ℓn : τ − n } |

α | τ −

1 ⊓ τ − 2 | ⊤ | µα.τ −

slide-47
SLIDE 47

Cases of unification

In HM inference, unification happens in three situations:

◮ Unifying two input types ◮ Unifying two output types ◮ Using the output of one expression as input to

another

slide-48
SLIDE 48

Cases of unification

In HM inference, unification happens in three situations:

◮ Unifying two input types

Introduce ⊔

◮ Unifying two output types

Introduce ⊓

◮ Using the output of one expression as input to

another τ + ≤ τ − constraint

slide-49
SLIDE 49

Eliminating variables, ML-style

Suppose we have an identity function α → α

slide-50
SLIDE 50

Eliminating variables, ML-style

Suppose we have an identity function, which uses its argument as a τ α → α | α = τ

slide-51
SLIDE 51

Eliminating variables, ML-style

Suppose we have an identity function, which uses its argument as a τ α → α | α = τ ≡∀ τ → τ

slide-52
SLIDE 52

Eliminating variables, ML-style

Suppose we have an identity function, which uses its argument as a τ α → α | α = τ ≡∀ τ → τ The substitution [τ/α] solves the constraint α = τ

slide-53
SLIDE 53

“solves?”

What does it mean to solve a constraint?

  • 1. [τ/α] trivialises the constraint α = τ

(it is a unifier), and all other unifiers are an instance of it (it is a most general unifier)

slide-54
SLIDE 54

“solves?”

What does it mean to solve a constraint?

  • 1. [τ/α] trivialises the constraint α = τ

(it is a unifier), and all other unifiers are an instance of it (it is a most general unifier)

  • 2. For any type τ ′, the following sets agree:

the instances of τ ′, subject to α = τ the instances of [τ/α]τ ′

slide-55
SLIDE 55

Definition 2, now with subtyping

Suppose we have an identity function, which uses its argument as a τ −. α → α | α ≤ τ −

slide-56
SLIDE 56

Definition 2, now with subtyping

Suppose we have an identity function, which uses its argument as a τ −. α → α | α ≤ τ − ≡∀ (α ⊓ τ −) → (α ⊓ τ −)

slide-57
SLIDE 57

Definition 2, now with subtyping

Suppose we have an identity function, which uses its argument as a τ −. α → α | α ≤ τ − ≡∀ (α ⊓ τ −) → (α ⊓ τ −) ≡∀ (α ⊓ τ −) → α

slide-58
SLIDE 58

Definition 2, now with subtyping

Suppose we have an identity function, which uses its argument as a τ −. α → α | α ≤ τ − ≡∀ (α ⊓ τ −) → (α ⊓ τ −) ≡∀ (α ⊓ τ −) → α The bisubstitution [α ⊓ τ −/α−] solves α ≤ τ −

slide-59
SLIDE 59

Decomposing constraints

We only need to decompose constraints of the form τ + ≤ τ −. τ1 ⊔ τ2 ≤ τ3 ≡ τ1 ≤ τ3, τ2 ≤ τ3 τ1 ≤ τ2 ⊓ τ3 ≡ τ1 ≤ τ2, τ1 ≤ τ3 Thanks to the input/output type distinction, the hard cases of τ1 ⊓ τ2 ≤ τ3 and τ1 ≤ τ2 ⊔ τ3 can never come up.

slide-60
SLIDE 60

Combining solutions

We solve a system of multiple constraints C1, C2 by:

◮ Solving C1, giving a bisubstitution ξ ◮ Applying that to C2 ◮ Solving ξC2, giving a bisubstitution ζ

Then ξ ◦ ζ solves the system C1, C2.

slide-61
SLIDE 61

Putting it all together

biunify(C) takes a set of constraints C, and produces a bisubstitution solving them. biunify(∅) = [] biunify(α ≤ α, C) = biunify(C) biunify(α ≤ τ, C) = biunify(θα≤τH; θα≤τ C) ◦ θα≤τ biunify(τ ≤ α, C) = biunify(θτ≤αH; θτ≤α C) ◦ θτ≤α biunify(c, C) = biunify(decompose(c), C)

slide-62
SLIDE 62

Putting it all together

biunify(C) takes a set of constraints C, and produces a bisubstitution solving them. biunify(∅) = [] biunify(α ≤ α, C) = biunify(C) biunify(α ≤ τ, C) = biunify(θα≤τH; θα≤τ C) ◦ θα≤τ biunify(τ ≤ α, C) = biunify(θτ≤αH; θτ≤α C) ◦ θτ≤α biunify(c, C) = biunify(decompose(c), C) Replace the ≤ with = and we have Martelli and Montanari’s unification algorithm.

slide-63
SLIDE 63

Summary

MLsub infers types by walking the syntax of the program, but must deal with subtyping constraints rather than just equalities. Thanks to:

◮ algebraically well-behaved types ◮ polar types, restricting occurrences of ⊔ and ⊓ ◮ a careful definition of “solves”

the biunify algorithm can always handle these constraints, producing a principal type.

slide-64
SLIDE 64

Questions? http://www.cl.cam.ac.uk/~sd601/mlsub stephen.dolan@cl.cam.ac.uk

slide-65
SLIDE 65

Mutable references

References are generally considered “invariant”. Instead, consider ref a two-argument constructor (α, β) ref with operations: make : (α, α) ref get : (⊥, β) ref → β set : (α, ⊤) ref → α → unit