1/38 The gradual typing approach to mixing static and dynamic typing Jeremy G. Siek University of Colorado =) Indiana University TFP 2013 at Provo, Utah, May 2013
The Goals of Gradual Typing I Enjoy the benefits of static & dynamic typing in different parts of the same program. I Provide seamless interoperability between the static & dynamic parts. Static Typing Reliability & Efficiency Dynamic Typing Productivity 2/38
Overview Gradual Typing: I Basics I History I Functions I Objects I Generics I Mutable State I The Future 3/38
How can Static and Dynamic Coexist? 1 def abs(n: int) int: return -n if n<0 else n def dist(x, y): return abs(x - y) 1 Gradual Typing for Functional Languages, Siek and Taha, SFP 2006. 4/38
How can Static and Dynamic Coexist? 2 Consistency: def abs(n: int) int: return -n if n<0 else n T? int int? T str str def dist(x :?, y :?)?: return abs(x - y)? int T 1 T 3 T 2 T 4 T 1! T 2 T 3! T 4 Type rule for application: ` e 1 : T 1! T 3 ` e 2 : T 2 T 1 T 2 ` e 1 e 2 : T 3 2 Gradual Typing for Functional Languages, Siek and Taha, SFP 2006. 5/38
6/38 Properly Catch Static Errors def abs(n: int) int: return -n if n<0 else n x : str = input_string()... abs(x) str 6 int Consistency: T? int int? T str str T 1 T 3 T 2 T 4 T 1! T 2 T 3! T 4
Compiler Performs Cast Insertion def abs(n: int) int: return -n if n<0 else n def dist(x :?, y :?)?: return abs(x - y :? ) int) : int )? dist(7 : int )?, 3 : int )?) The dynamic semantics specifies the behavior of casts, e.g., (7 : int )?) :? ) int! 7 (7 : int )?) :? ) str! error 7/38
Overview Gradual Typing: I Basics I History I Functions I Objects I Generics I Mutable State I The Future 8/38
The Prehistory of Gradual Typing Lisp, early 1980 s types as optimization hints Abadi et al., 1991 defined a dynamic type with explicit injection and projection terms. Cartwright & Fagan, 1991 soft typing: static analysis of dynamic programs Henglein, 1992 expressed dynamic casts using an algebra of coercions. Thatte, 1994 introduced implicit casts based on subtyping, which didn t quite work. Findler & Felleisen, 2002 designed contracts for higher-order functions. 9/38
History of Gradual Typing (Abbreviated) *Siek & Taha, 2006 implicit casts, consistency Herman et al., 2006 space-efficient casts *Siek & Taha, 2007 gradual typing & objects Adobe, 2006 ActionScript becomes gradual Sam TH & Felleisen, 2008 Typed Scheme Wadler & Findler, 2009 the Blame Theorem *Garcia et. al, 2009 space-efficient blame Larry Wall, 2009 Perl 6 becomes gradual Bierman et al, 2010 C# becomes gradual *Ahmed, et al., 2011 gradual typing & generics Hejlsberg, 2012 Microsoft releases TypeScript *Siek et al., 2012 gradual typing & mutable state 10 / 38
Overview Gradual Typing: I Basics I History I Functions I Objects I Generics I Mutable State I The Future 11 / 38
12 / 38 Higher-Order Functions are Hard def deriv(d: float, f: float!float) float: return lambda(x:float): (f(x+d)-f(x-d)) / (2.0*d) 1 def g(y): 2 if y > 0: 3 return y**3 - y - 1 4 else: 5 return "yikes" 6 7 deriv(0.01, g)(3.0) 8 deriv(0.01, g)(-3.0)
Higher-Order Functions and Blame def deriv(d: float, f: float!float) float: return lambda(x:float): (f(x+d)-f(x-d)) / (2.0*d) 1 def g(y): 2 if y > 0: 3 return y**3 - y - 1 4 else: 5 return "yikes" 6 7 deriv(0.01, g)(3.0) 8 deriv(0.01, g)(-3.0) Casting a function creates a wrapper : g :?!? ) 8 float! float p : float.! g (p : float ) 8?) :? ) 8 float yikes :str )? ) 8 float! blame 8 13 / 38
14 / 38 Is Gradual Typing Unsound? 3 4 adding type annotations at random places is unsound Matthias Felleisen, PLT Mailing List, June 10, 2008.
14 / 38 Is Gradual Typing Unsound? 3 4 adding type annotations at random places is unsound Matthias Felleisen, PLT Mailing List, June 10, 2008. Too weak: If ` e : T, then either e diverges, e e! error.! v and ` v : T, or
14 / 38 Is Gradual Typing Unsound? 3 4 adding type annotations at random places is unsound Matthias Felleisen, PLT Mailing List, June 10, 2008. Too weak: If ` e : T, then either e diverges, e e! error.! v and ` v : T, or Too strong: If ` e : T, then either e diverges or e! v and ` v : T.
Is Gradual Typing Unsound? 3 4 adding type annotations at random places is unsound Matthias Felleisen, PLT Mailing List, June 10, 2008. Too weak: If ` e : T, then either e diverges, e e! error.! v and ` v : T, or Too strong: If ` e : T, then either e diverges or e! v and ` v : T. Just right: If ` e : T, then either e diverges, e! v and ` v : T, or e! blame ` where e = C[e 0 : T 1 )` T 2 ] and T 1 6<: T 2. 3 Well-typed Program s Can t be Blamed, Wadler & Findler, ESOP 2009 4 Exploring the Design Space of H.O. Casts, Siek et al., ESOP 2009 14 / 38
15 / 38 Blame and Subtyping Wadler & Finder, 2009: int <: int?<:? int <:? T <:??! T <:? T 3 <: T 1 T 2 <: T 4 T 1!T 2 <: T 3!T 4 Siek, Garcia, & Taha, 2009: int <: int T <:? T 3 <: T 1 T 2 <: T 4 T 1!T 2 <: T 3!T 4
But Wrappers are Not Space Efficient 5 def even(n: int, k:?! Bool) Bool: if n == 0: return k(true) else: return odd(n - 1, k) def odd(n: int, k: Bool!Bool) Bool: if n == 0: return k(false) else: return even(n - 1, k) 5 Space-Efficient Gradual Typing. Herman, et al., TFP 2006 16 / 38
17 / 38 Toward Efficient Casts: Reified Wrappers Regular wrappers: v ::=... x : T. e v : T 1!T 2 ) T 3!T 4! x : T 3. (v (x : T 3 ) T 1 )) : T 2 ) T 4 Reified wrappers: + v ::=... x : T. e v : T 1!T 2 ) T 3!T 4 (v 1 : T 1!T 2 ) T 3!T 4 ) v 2! (v 1 (v 2 : T 3 ) T 1 )) : T 2 ) T 4
Compressing a Sequence of Casts 6 v :?!? )?! str!? ) int!?!? )?!?! int v :?!? ) int! str! int )?!?! int Define an information ordering:? v T int v int str v str T 1 v T 3 T 2 v T 4 T 1! T 2 v T 3! T 4 Take the least upper bound to obtain a triple : e : T 1 ) T 2 ) )T n 1 ) T n e : T 1 )t{t 2,...,T n 1 })T n 6 Threesomes, with and without blame. Siek & Wadler, POPL 2010. 18 / 38
19 / 38 Space Efficiency Notation: e erases the casts from e. Theorem (Space Efficiency) For any program e there is a constant factor c such that if e 7! e 0, then size(e 0 ) apple c size( e 0 ).
Overview Gradual Typing: I Basics I History I Functions I Objects I Generics I Mutable State I The Future 20 / 38
21 / 38 Gradual Typing and Subtyping At the heart of most OO languages is a subsumption rule: ` e : T 1 T 1 <: T 2 ` e : T 2 Thatte s early attempt at gradual typing didn t use consistency but instead put the dynamic type at the top and bottom of the subtyping relation. T <:?? <: T
22 / 38 The Problem with Subtyping Subtyping is transitive, so we have: str <:?? <: int str <: int In general, for any types T 1 and T 2 we have T 1 <: T 2. So the type checker accepts all programs! (Even ones that get stuck.)
Consistency and Subtyping are Orthogonal 7 Let subtyping deal with object types: [l i : T i i21..n+m ] <: [l i : T i i21..n ]?<:? Let consistency deal with the dynamic type: T? Include the subsumption rule? T ` e : T 1 T 1 <: T 2 ` e : T 2 and use consistency instead of equality: ` e 1 : T 1! T 3 ` e 2 : T 2 T 1 T 2 ` e 1 e 2 : T 3 7 Gradual Typing for Objects, Siek and Taha, ECOOP 2007 23 / 38
24 / 38 An Algorithmic Type System The usual trick is to remove the subsumption rule and use subtyping in place of equality. ` e 1 : T 1! T 3 ` e 2 : T 2 T 2 <: T 1 ` e 1 e 2 : T 3 but for gradual typing, this would look like ` e 1 : T 1! T 3 ` e 2 : T 2 T 2 <: T1 0 T1 0 T 1 ` e 1 e 2 : T 3 which is not syntax directed. We need a relation that composes the two: ` e 1 : T 1! T 3 ` e 2 : T 2 T 2. T 1 ` e 1 e 2 : T 3
25 / 38 Consistent-Subtyping T.??. T int. int str. str T 3. T 1 T 2. T 4 T i. Ti 0 8i 2 1..n T 1! T 2. T 3! T 4 [l i : T i21..n+m i ]. [l i : T 0 (This is a more direct definition than the one I gave in Gradual Typing for Objects.) i i21..n ]
Overview Gradual Typing: I Basics I History I Functions I Objects I Generics I Mutable State I The Future 26 / 38
27 / 38 Gradual Typing & Polymorphism Review of System F: T ::=... 8X. T e ::=... X. e e[t ] ( X. e)[t ]! e[x:=t ], X ` e : T ` X. e : 8X. T ` e : 8X. T 1 ` e[t 2 ]:T 1 [X:=T 2 ]
Parametric Polymorphism (Generics) Ahmed, Findler, and Wadler proposed a design at STOP 2009. Their goals: I Seamless interoperability. v :(8X. S) ) T! v[?] :S[X:=?] ) T v : S ) (8X. T )! X. (v : S ) T ) I Retain relational parametricity (i.e., theorems for free). I Provide a natural subtyping relation and blame theorem. I helped refine the design for the POPL 2011 paper. 28 / 38
Challenges to Parametricity Consider two casts: K? =( x:?. y:?.x) :?!?!? )? K? :? ) m 8X.8Y. X! Y! X K? :? )` 8X.8Y. X! Y! Y The second cast should lead to a cast failure. But a naive semantics lets it go through. (K? :? )` 8X.8Y. X! Y! Y )[int][int] 23! (K? :? )` int! int! int) 23! 2 : int )? )` int! 2 29 / 38
Enforcement of Parametricity (K? :? )` 8X.8Y. X! Y! X)[int][int] 23! ( X:=int. Y :=int. K? :? )` X! Y! X) 23! ( X:=int. Y :=int. 2 : X )? )` X)! 2 (K? :? )` 8X.8Y. X! Y! Y )[int][int] 23! ( X:=int. Y :=int. K? :? )` X! Y! Y ) 23! ( X:=int. Y :=int. 2 : X )? )` Y )! blame ` This mechanism should work, but the parametricity theorem is an open problem. 30 / 38
Overview Gradual Typing: I Basics I History I Functions I Objects I Generics I Mutable State I The Future 31 / 38
32 / 38 Gradual Typing & Mutable State Consider ML-style references T ::=... ref T e ::=... ref e e := e!e with a permissive rule for consistency of reference types: T 1 T 2 ref T 1 ref T 2
33 / 38 The Standard Semantics Incurs Overhead The Herman TFP 2006 semantics induces overhead, even in statically-typed regions of code. a 2 N v ::=... a v : ref T 1 ) ref T 2 ref v µ 7! a µ(a := v) if a /2 dom(µ) ( µ(a) µ if v = a!v µ 7! (!v 0 ):T 1 ) T 2 µ if v = v 0 : ref T 1 ) ref T 2 ( v 2 µ(a := v 2 ) if v 1 = a v 1 := v 2 µ 7! v1 0 := (v 2:T 2 )T 1 ) µ if v 1 = v1 0 : ref T 1)ref T 2
% Monotonic References e ::=... ref e e := e!e e := e@t!e@t v ::=... a let r1 = ref (42 : int )?) in let r2 = r1 : ref? ) ref int in (!r1@?,!r2) ref? ref? +3ref int ( (42 : int )?,?) (42, int) 34 / 38
35 / 38 Standard vs. Monotonic 1 let r1 = ref (1 : int )?) in 2 let r2 = r1 : ref? ) ref int in 3 let r3 = r1 : ref? ) ref bool in 4 let x =!r2 in 5 r3 := true; 6 let y =!r3 in 7 (x,y) 7! (1, true) (standard) 7! blame 3 (monotonic)
e µ! e 0 Monotonic References ref T v µ! a a := (v, T ) if a /2 dom(µ)!a µ! µ(a) 1 a := v µ! a a := (v,µ(a) 2 ) a : ref T 1 ) ref T 2 µ! error if T 2 6 µ(a) 2 a : ref T 1 ) ref T 2 µ! a if T 2 v µ(a) 2 a : ref T 1 ) ref T 2 µ! a a := (e,µ(a) 2 t T 2 ) if T 2 6v µ(a) 2, e = µ(a) 1 : µ(a) 2 ) µ(a) 2 t T 2!a@T µ! (µ(a) 1 : µ(a) 2 ) T ) a := v@t µ! a a := (v : T ) µ(a) 2,µ(a) 2 ) e µ 7! e 0 µ 0 e µ! e 0 e µ 7! e 0 (µ) µ(a)=(e 1, T ) e 1 µ! e 0 1 e µ 7! e (µ(a := (e 0 1, T ))) 36 / 38
Overview Gradual Typing: I Basics I History I Functions I Objects I Generics I Mutable State I The Future 37 / 38
The Future I Gradually-typed Python (Michael Vitousek) I Monotonic references with blame (some ideas, not easy) I Monotonic objects (draft) I Putting it all together, e.g. can we maintain space efficiency with polymorphic blame? (no idea) I Parametricity for the Polymorphic Blame Calculus (Amal Ahmed is part way there) I Compiling and optimizing gradually-typed programs (e.g. Rastogi, Chaudhuri, and Hosmer, POPL 2012) Questions? 38 / 38