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 ∀ α. ( α → bool ) → α → α → α
Data flow in select select p v d = if ( p v ) then v else d v d argument to p result
Data flow in select select p v d = if ( p v ) then v else d v d argument to p result In MLsub, select has this type scheme: ∀ α, β. ( α → bool ) → α → β → ( α ⊔ β )
Γ ⊢ e : τ
Γ ⊢ e : τ
Expressions of MLsub We have functions x λ x . e e 1 e 2 ... and records { ℓ 1 = e 1 , . . . , ℓ n = e n } e .ℓ ... and booleans true false if e 1 then e 2 else e 3 ... and let ˆ let ˆ x = e 1 in e 2 x
Γ ⊢ e : τ
Typing rules of MLsub MLsub is ML + Γ ⊢ e : τ 1 ( Sub ) τ 1 ≤ τ 2 Γ ⊢ e : τ 2
Γ ⊢ e : τ
Constructing Types The standard definition of types looks like: τ ::= ⊥ | τ → τ | ⊤ (ignoring records and booleans for now)
Constructing Types The standard definition of types looks like: τ ::= ⊥ | τ → τ | ⊤ (ignoring records and booleans for now) with a subtyping relation like: τ ′ τ 2 ≤ τ ′ 1 ≤ τ 1 2 τ 1 → τ 2 ≤ τ ′ 1 → τ ′ ⊥ ≤ τ τ ≤ ⊤ 2
Lattices These types form a lattice: ◮ least upper bounds τ 1 ⊔ τ 2 ◮ greatest lower bounds τ 1 ⊓ τ 2
Lattices These types form a lattice: ◮ least upper bounds τ 1 ⊔ τ 2 ◮ greatest lower bounds τ 1 ⊓ τ 2 e 1 : τ 1 e 2 : τ 2 if rand () then e 1 else e 2 : τ 1 ⊔ τ 2
Bizzarely difficult questions Is this true, for all α ? α → α ≤ ⊥ → ⊤
Bizzarely difficult questions Is this true, for all α ? α → α ≤ ⊥ → ⊤ How about this? ( ⊥ → ⊤ ) → ⊥ ≤ ( α → ⊥ ) ⊔ α
Bizzarely difficult questions Is this true, for all α ? α → α ≤ ⊥ → ⊤ How about this? ( ⊥ → ⊤ ) → ⊥ ≤ ( α → ⊥ ) ⊔ α Yes, it turns out, by case analysis on α .
Bizzarely difficult questions Is this true, for all α ? α → α ≤ ⊥ → ⊤ How about this? ( ⊥ → ⊤ ) → ⊥ ≤ ( α → ⊥ ) ⊔ α Yes, it turns out, by case analysis on α . And only by case analysis.
Extensibility ◦ → τ 2 . Let’s add a new type of function τ 1
Extensibility ◦ → τ 2 . Let’s add a new type of function τ 1 It’s a supertype of τ 1 → τ 2 “function that may have side effects”
Extensibility ◦ → τ 2 . Let’s add a new type of function τ 1 It’s a supertype of τ 1 → τ 2 “function that may have side effects” Now we have a counterexample: ◦ ◦ α = ( ⊤ → ⊥ ) → ⊥
Extensible type systems Two techniques give us an extensible system: ◮ Add explicit type variables as indeterminates gets rid of case analysis
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
Combining types How to combine different types into a single system? τ ::= bool | τ 1 → τ 2 | { ℓ 1 : τ 1 , . . . , ℓ n : τ n }
Combining types How to combine different types into a single system? τ ::= bool | τ 1 → τ 2 | { ℓ 1 : τ 1 , . . . , ℓ n : τ n } We should read ‘ | ’ as coproduct
Concrete syntax Build an actual syntax for types, by writing down all the operations on types: τ ::= bool | τ 1 → τ 2 | { ℓ 1 : τ 1 , . . . , ℓ n : τ n } | α | ⊤ | ⊥ | τ ⊔ τ | τ ⊓ τ
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.
Resulting types We end up with all the standard types
Resulting types We end up with all the standard types ... with the same subtyping order
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
Γ ⊢ e : τ
Principality in ML Intuitively, For any e typeable under Γ , there’s a best type τ
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 σ Γ , τ .
Reformulating the typing rules The complexity arises because Γ is part question, part answer.
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 [∆] τ
Π � e : [∆] τ
question answer ���� � �� � Π � e : [∆] τ
Subsumption Define ≤ ∀ as the least relation closed under: ◮ Instatiation , replacing type variables with types ◮ Subtyping , replacing types with supertypes
Principality in MLsub A principal typing scheme for e under Π is a [∆] τ that subsumes any other.
The choose function choose takes two values and returns one of them: choose : ∀ α.α 1 → α 2 → α 3
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.
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 : ∀ αβ.α → β → α ⊔ β
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
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.
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 τ −
Polar types τ + ::= bool | τ − 1 → τ + 2 | { ℓ 1 : τ + 1 , . . . , ℓ n : τ + n } | α | τ + 1 ⊔ τ + 2 | ⊥ | µα.τ + τ − ::= bool | τ + 1 → τ − 2 | { ℓ 1 : τ − 1 , . . . , ℓ n : τ − n } | α | τ − 1 ⊓ τ − 2 | ⊤ | µα.τ −
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
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
Eliminating variables, ML-style Suppose we have an identity function α → α
Eliminating variables, ML-style Suppose we have an identity function, which uses its argument as a τ α → α | α = τ
Eliminating variables, ML-style Suppose we have an identity function, which uses its argument as a τ α → α | α = τ ≡ ∀ τ → τ
Eliminating variables, ML-style Suppose we have an identity function, which uses its argument as a τ α → α | α = τ ≡ ∀ τ → τ The substitution [ τ/α ] solves the constraint α = τ
“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 )
“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 [ τ/α ] τ ′
Definition 2, now with subtyping Suppose we have an identity function, which uses its argument as a τ − . α → α | α ≤ τ −
Definition 2, now with subtyping Suppose we have an identity function, which uses its argument as a τ − . α → α | α ≤ τ − ≡ ∀ ( α ⊓ τ − ) → ( α ⊓ τ − )
Definition 2, now with subtyping Suppose we have an identity function, which uses its argument as a τ − . α → α | α ≤ τ − ≡ ∀ ( α ⊓ τ − ) → ( α ⊓ τ − ) ≡ ∀ ( α ⊓ τ − ) → α
Recommend
More recommend