Ravi Chugh Motivation Dynamic Languages Dynamicallytyped languages Enable rapid prototyping Facilitate interlanguage development Staticallytyped languages Prevent certain runtime errors Enable optimized execution ID: 637989
Download Presentation The PPT/PDF document "Type Inference with Run-time Logs" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
Type Inferencewith Run-time Logs
Ravi ChughSlide2
Motivation: Dynamic Languages
Dynamically-typed languages
Enable rapid prototyping
Facilitate inter-language developmentStatically-typed languagesPrevent certain run-time errorsEnable optimized executionProvide checked documentationMany research efforts to combine bothRecent popularity of Python, Ruby, and JavaScript has stoked the fire
2Slide3
Static Type Systems
Many attempts at fully static type systems
Flow sensitive types, occurrence types, ...
Often require refactoring and annotationSome features just cannot be statically typedRetrofitting existing language unlikelyCould work for a new languageBut we want to migrate existing programs
3Slide4
Gradual Type Systems
Typing is not all-or-nothing
Some expressions have types, others are
dynamicTyped portions checked in standard waysUntyped portions fall back on run-time checksChallengesGranularityGuaranteesBlame trackingand ...
4Slide5
... Inference!
Goal: migrate programs from dynamic langs
Programmer annotation burden is currently 0
A migration strategy must require ~0 workEven modifying 1-10% LOC in a large codebasecan be prohibitive5Slide6
The Challenge: Inference
6
Goal: practical type system
polymorphism
def id(x) { return x };
1 + id(2);
“hello” ^ id(“world”);
polymorphism
id : ∀X. X → XSlide7
The Challenge: Inference
7
Goal: practical type system
subtyping
polymorphism
def succA(x) {
return (1 + x.a)
};
succA({a=1});
succA({a=1;b=“hi”});
subtyping
{a:Int;b:Str} <: {a:Int}Slide8
The Challenge: Inference
bounded quantification
8
Goal: practical type system
subtyping
polymorphism
def incA(x) {
x.a := 1 + x.a;
return x
};
incA({a=1}).a;
incA({a=1;b=“hi”}).b;
bounded quantification
incA
:
∀X<:{a:Int}.
X
→
XSlide9
The Challenge: Inference
bounded quantification
9
Goal: practical type system
subtyping
polymorphism
dynamic
n := if b then 0
else “bad”;
m := if b then n + 1
else 0;
dynamic
n : dynamicSlide10
The Challenge: Inference
bounded quantification
10
Goal: practical type system
subtyping
polymorphism
dynamic
other features...Slide11
The Challenge: Inference
bounded quantification
11
Goal: practical type system
subtyping
polymorphism
dynamic
other features...
System ML
✓Slide12
The Challenge: Inference
bounded quantification
12
Goal: practical type system
subtyping
polymorphism
dynamic
other features...
System F
✗Slide13
The Idea
Use run-time executions to help inference
Program may have many valid types
But particular execution might rule out someDynamic language programmers test a lotUse existing test suites to help migration13Slide14
Route: Inference w/ Run-time Logs
bounded quantification
14
subtyping
+
polymorphism
omit higher-order functions
System E
System E
≤Slide15
First Stop
bounded quantification
15
subtyping
+
polymorphism
omit higher-order functions
System E
System E
≤
System E
−Slide16
Function definitions
Function calls
Program is sequence of function definitions
Typed vs. Untyped Syntax
16
def y[A
1
,...,A
n
](x:τ){ e }
def y(x){ e’ }
y[τ
1
,...,τ
n
](e)
y(e’)Slide17
E− Type System
Expression and function types
τ ::=
| Int | Bool | ... | {f
i
:τ
i
}
| X
σ ::= ∀X
i
. τ
1
→ τ
2
Typing rules prevent field-not-found and primitive operation errors
No rule for if-expressions yet
17Slide18
def id (x) { x }
def id[X] (x:X) { x }
: ∀X. X → X
def id[] (x:Int) { x } : Int → Int
def id[Y,Z] (x:Y*Z) { x }
: ∀Y,Z. Y*Z → Y*Z
Infinitely many valid types for
id
...
18Slide19
... but
∀X. X → X
is the principal type
19
∀X. X →
X
∀Y,Z. Y*Z → Y*Z
Int
→
IntSlide20
... but
∀X. X → X
is the principal type
More general than every other type
Allows
id
to be used in most different ways
20
∀Y,Z. Y*Z → Y*Z
∀X. X*X → X*X
Int*Int
→
Int*Int
Int*Bool
→
Int*Bool
∀X. X →
X
Int
→
IntSlide21
def readA (x) { x.a }
def readA[A] (x:{a:A}) { x.a }
: ∀A. {a:A} → A
This is the best type
21
∀A.
{a:A}
→
A
∀A,B. {a:A;b:B} → A
{a:Int}
→
Int
∀B. {a:Int;b:B} → IntSlide22
def foo (o) { let _ = o.a in o }
Two valid types:
Neither is better than the other
22
∀A.
{a:A}
→
{a:A}
∀A,B.
{a:A;b:B}
→
{a:A;b:B}
allows
foo({a=1;b=2}).b
allows
foo({a=1})Slide23
E− Static Type Inference
E
−
lacks principal typesCannot assign type just from definitionNeed to consider calling contextsOur approach is iterativeImpose minimal constraints on argumentIf a calling context requires an additional field to be tracked, backtrack and redo the function
23Slide24
24
τ ::=
| Int | Bool | ...
| {fi:τ
i
}
| X
Annotated TypesSlide25
25
“this type variable is for parameter of
y
and then projected on sequence of fields
l
”
τ ::=
| Int | Bool | ...
| {f
i
:τ
i
}
| X
y.l
Annotated TypesSlide26
26
def id (x) { x }
τ ::=
| Int | Bool | ...
| {f
i
:τ
i
}
| X
y.l
Annotated Types
∀X. X → X
∀X
id
. X
id
→ X
idSlide27
27
def readA (x) { x.a }
τ ::=
| Int | Bool | ...
| {f
i
:τ
i
}
| X
y.l
Annotated Types
∀A. {a:A} → A
∀X
readA.a
. {a:X
readA.a
} → X
readA.aSlide28
28
def readAB (x) { x.a.b }
τ ::=
| Int | Bool | ...
| {f
i
:τ
i
}
| X
y.l
Annotated Types
∀AB. {a:{b:AB}} → AB
∀X
readA.a.b
.
{a:{b:X
readA.a.b
}} → X
readA.a.bSlide29
29
τ ::=
| Int | Bool | ...
| {fi:τ
i
}
| X
y.l
Annotated TypesSlide30
30
“this record type came from parameter of
y
and
then projected on sequence of fields
l
”
τ ::=
| Int | Bool | ...
| {f
i
:τ
i
} | {f
i
:τ
i
}
y.l
| X
y.l
Annotated TypesSlide31
31
def foo (o) { let _ = o.a in o }
Annotated Types
∀A. {a:A} → {a:A}
∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
τ ::=
| Int | Bool | ...
| {f
i
:τ
i
} | {f
i
:τ
i
}
y.l
| X
y.lSlide32
32
def foo (o) { let _ = o.a in o }
Annotated Types
∀A,B. {a:A;b:B} → {a:A;b:B}
∀X
foo.a
,X
foo.b
. {a:X
foo.a
;b:X
foo.b
} →
{a:X
foo.a
;b:X
foo.b
}
foo
τ ::=
| Int | Bool | ...
| {f
i
:τ
i
} | {f
i
:τ
i
}
y.l
| X
y.lSlide33
33
def foo (o) { let _ = o.a in o }
Iterative Inference – Example 1
Iteration 0
Processing
foo
...
X
foo
<: {a : X
foo.a
}
foo : ∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
def main () { foo({a=1}) }Slide34
34
def foo (o) { let _ = o.a in o }
def main () { foo({a=1}) }
Iterative Inference – Example 1
Iteration 0
Processing
main
...
{a=1} : {a:Int}
foo : ∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
✓
{a:Int} ≤ {a:X
foo.a
}
?Slide35
35
def foo (o) { let _ = o.a in o }
Iterative Inference – Example 2
Iteration 0
Processing
foo
...
X
foo
<: {a : X
foo.a
}
foo : ∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
def main () { let z = {a=1;b=2} in foo(z).b }Slide36
36
def foo (o) { let _ = o.a in o }
def main () { let z = {a=1;b=2} in foo(z).b }
Iterative Inference – Example 2
Iteration 0
Processing
main
...
z : {a:Int;b:Int}
foo : ∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
✓
{a:Int;b:Int} ≤ {a:X
foo.a
}
?Slide37
✗
*
37
def foo (o) { let _ = o.a in o }
def main () { let z = {a=1;b=2} in foo(z).b }
Iterative Inference – Example 2
Iteration 0
Processing
main
...
foo(z) : {a:Int}
foo
foo : ∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
{a:Int}
foo
≤ {b:X}
?
✗
Caller-induced constraints
X
foo
<: {b : X
foo.b
}
z : {a:Int;b:Int}
{a:Int;b:Int} ≤ {a:X
foo.a
}
?Slide38
38
def foo (o) { let _ = o.a in o }
def main () { let z = {a=1;b=2} in foo(z).b }
Iterative Inference – Example 2
Iteration 1
Processing
foo
...
foo : ∀X
foo.a
. {a:X
foo.a
;b:X
foo.b
} → {a:X
foo.a
;b:X
foo.b
}
foo
X
foo
<: {a : X
foo.a
}
Caller-induced constraints
X
foo
<: {b : X
foo.b
}Slide39
39
def foo (o) { let _ = o.a in o }
def main () { let z = {a=1;b=2} in foo(z).b }
Iterative Inference – Example 2
Iteration 1
foo : ∀X
foo.a
. {a:X
foo.a
;b:X
foo.b
} → {a:X
foo.a
;b:X
foo.b
}
foo
Caller-induced constraints
X
foo
<: {b : X
foo.b
}
Processing
main
...
z : {a:Int;b:Int}
✓
{a:Int;b:Int} ≤ {a:X
foo.a
;b:X
foo.b
}
?Slide40
40
def foo (o) { let _ = o.a in o }
def main () { let z = {a=1;b=2} in foo(z).b }
Iterative Inference – Example 2
Iteration 1
foo : ∀X
foo.a
. {a:X
foo.a
;b:X
foo.b
} → {a:X
foo.a
;b:X
foo.b
}
foo
Caller-induced constraints
X
foo
<: {b : X
foo.b
}
Processing
main
...
foo(z) : {a:Int;b:Int}
foo
{a:Int;b:Int}
foo
≤ {b:X}
?
✓
z : {a:Int;b:Int}
{a:Int;b:Int} ≤ {a:X
foo.a
;b:X
foo.b
}
?Slide41
def main () { let _ = foo({a=1}) in
let z = {a=1;b=2} in foo(z).b }
41
def foo (o) { let _ = o.a in o }
Iterative Inference – Example 3
Iteration 0
foo : ∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
✓
✓Slide42
def main () { let _ = foo({a=1}) in
let z = {a=1;b=2} in foo(z).b }
42
def foo (o) { let _ = o.a in o }
Iterative Inference – Example 3
Iteration 0
foo : ∀X
foo.a
. {a:X
foo.a
} → {a:X
foo.a
}
foo
✗
*Slide43
43
Iterative Inference – Example 3
Iteration 1
foo : ∀X
foo.a
. {a:X
foo.a
;b:X
foo.b
} → {a:X
foo.a
;b:X
foo.b
}
foo
✗
Calling contexts impose incompatible
constraints on type of
foo
def main () { let _ = foo({a=1}) in
let z = {a=1;b=2} in foo(z).b }
def foo (o) { let _ = o.a in o }Slide44
E− Static Type Inference
In iteration 0, don’t have calling context info
Iteratively constraint
Xfoo as neededProcess foo and its callers again
No principal types, but still static inference
Can run-time information improve algorithm?
44Slide45
E− Type Inference with Run-time Logs
Rig evaluation to log caller-induced constraints
Wrap all values with sets of type variables
When a value passed to function y
, add
X
y
tag
When a value with tag
X
y.l
projected on field
f
, record
X
y.l
<: {f : X
y.l.f
}
Iteration 0 of inference looks in log forcaller-induced constraints
45Slide46
46
def foo (o) { let _ = o.a in o }
def main () { let z = {a=1;b=2} in foo(z).b }
main()
⇒ let z = {a=1;b=2} in foo(z).b
⇒ let z = [{a=1;b=2},
{}
] in foo(z).b
⇒ foo([{a=1;b=2},
{X
foo
}
]).b
⇒ (let _ = [{a=1;b=2},
{X
foo
}
].a in [{a=1;b=2},
{X
foo
}
]).b
⇒ (let _ = [1,
{X
foo.a}
] in [{a=1;b=2},{Xfoo}]).b
⇒ [{a=1;b=2},
{X
foo
}
].b
⇒ [2,
{X
foo.b
}
]
Evaluation
Run-time log
X
foo
<: {b : X
foo.b
}
X
foo
<: {a : X
foo.a
}
Caller-induced
constraintSlide47
System E− Summary
47
Fully static inference needs to iterate
Can wrap run-time values with sets of type variables and record field read constraints
If all expressions executed, then
log contains all caller-induced constraints
no need for iterationSlide48
Next Stop
bounded quantification
48
subtyping
+
polymorphism
System E
System E
System E
≤
System E
−Slide49
E Type System
Type of if-expression is join of branch types
49
if b then 1 else 2
if b then 1 else true
if b then {f=1; g=“”}
else {f=2; h=true}
if b then {f=“”}
else {f=true}
: Int
: {f:Int}
✗
✗Slide50
def bar (x,y) {
if
1
x.n > 0 then x else y }
50
def bar (o) {
if
1
o.1.n > 0 then o.1 else o.2 }
τ
1
*τ
2
shorthand for
{1:τ
1
;2:τ
2
}
shorthand forSlide51
def bar (x,y) {
if
1
x.n > 0 then x else y }Three valid incomparable types:
51
∀A.
{n:Int}*{}
→
{}
∀A.
{n:Int}*{n:Int}
→
{n:Int}
∀B.
{n:Int;b:B}*{n:Int;b:B}
→
{n:Int;b:B}Slide52
τ ::=
| Int | Bool | ...
| {f
i:τi} | {fi
:τ
i
}
y.l
| X
y.l
52
New Annotated TypesSlide53
τ ::=
| Int | Bool | ...
| {f
i:τi} | {fi
:τ
i
}
y.l
| {f
i
:τ
i
}
k.l
| X
y.l
53
“this record type came from if-expression
k
and
then projected on sequence of fields
l
”
New Annotated TypesSlide54
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
54Slide55
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
Iteration
Constraints from
bar
X
bar.1
<: {n : X
bar.1.n
}
0
55
New caller-induced constraints from
main
Type for
barSlide56
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
Iteration
Constraints from
bar
X
bar.1.n
= Int
0
X
bar.1
<: {n : X
bar.1.n
}
56
New caller-induced constraints from
main
Type for
barSlide57
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
Iteration
Constraints from
bar
X
bar.1.n
= Int
X
bar.1
X
bar.2
Π
0
X
bar.1
<: {n : X
bar.1.n
}
57
New caller-induced constraints from
main
Type for
barSlide58
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
Iteration
Constraints from
bar
X
bar.1.n
= Int
X
bar.1
X
bar.2
Π
0
X
bar.1
<: {n : X
bar.1.n
}
58
New caller-induced constraints from
main
Type for
bar
{n:Int}
* {} → {}
1
Slide59
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
Iteration
Constraints from
bar
X
bar.1.n
= Int
X
bar.1
X
bar.2
Π
X
bar.1
<: {n : X
bar.1.n
}
59
New caller-induced constraints from
main
Type for
bar
{n:Int}
* {} → {}
1
X
bar.1
<: {n : X
bar.1.n
}
X
bar.2
<: {n : X
bar.2.n
}
0Slide60
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
New caller-induced constraints from
main
Iteration
Constraints from
bar
Type for
bar
X
bar.1.n
= Int
X
bar.1
X
bar.2
Π
{n:Int}
* {} → {}
1
0
{n:Int}
* {n:Int} → {n:Int}
1
1
X
bar.1
<: {n : X
bar.1.n
}
X
bar.2
<: {n : X
bar.2.n
}
X
bar.1
<: {b : X
bar.1.b
}
X
bar.2
<: {b : X
bar.2.b
}
X
bar.1
<: {n : X
bar.1.n
}
60Slide61
def bar (x,y) { if
1
x.n > 0 then x else y }
def main () {
let o = bar({n=1;b=true},{n=2;b=false}) in
let _ = o.n + 1 in
not o.b }
New caller-induced constraints from
main
Iteration
Constraints from
bar
Type for
bar
X
bar.1.n
= Int
X
bar.1
X
bar.2
Π
{n:Int}
* {} → {}
1
0
{n:Int}
* {n:Int} → {n:Int}
1
1
X
bar.1
<: {n : X
bar.1.n
}
X
bar.2
<: {n : X
bar.2.n
}
∀B. {n:Int;b:B}
* {n:Int;b:B} →
{n:Int;b:B}
1
2
X
bar.1
<: {b : X
bar.1.b
}
X
bar.2
<: {b : X
bar.2.b
}
X
bar.1
<: {n : X
bar.1.n
}
61Slide62
System E Summary
Lacks principal types
Has iterative inference
Can run-time info help?Simple extension to if-expression evaluationRemoves need for iteration, as before62Slide63
Last Stop
bounded quantification
63
subtyping
+
polymorphism
System E
≤
System E
≤
System E
System E
−Slide64
Bounded Quantification
64
def y[ A
1
<:
τ
1
, ... , A
n
<:
τ
n
] (x:τ) { e }
Type parameters now have boundsSlide65
65
def foo (o) { let _ = o.a in o }
Now there is a best type for
foo
Variable
X
binds all other fields at call sites
X
is
{a:Int}
for
foo({a=1})
X
is
{a:Int;b:Int}
for
foo({a=1;b=2}).b
65
∀A.
{a:A}
→
{a:A}
∀A,B.
{a:A;b:B}
→
{a:A;b:B}
∀A, X<:{a:A}.
X
→
XSlide66
def bar (x,y) {
if
1
x.n > 0 then x else y }
66
∀X
bar.1
<:{n:Int}.
X
bar.1
*
X
bar.1
→
X
bar.1
{n:Int}*{}
→
{}
{n:Int}*{n:Int}
→
{n:Int}
∀B.
{n:Int;b:B}*{n:Int;b:B}
→
{n:Int;b:B}Slide67
def bar (x,y) {
if
1
x.n > 0 then x else y }
67
∀X
bar.1
<:{n:Int}.
X
bar.1
*
X
bar.1
→
X
bar.1
∀X
bar.1
<:{n:Int},X
bar.2
<:{}.
Xbar.1
*
X
bar.2
→
{}
{n:Int}*{}
→
{}Slide68
def bar (x,y) {
if
1
x.n > 0 then x else y }
68
∀X
bar.1
<:{n:Int}.
X
bar.1
*
X
bar.1
→
X
bar.1
∀X
bar.1
<:{n:Int},X
bar.2
<:{}.
Xbar.1
*
X
bar.2
→
{}
allows
bar
({n=1},{n=2}).n
allows
bar
({n=1},{})
and
bar
({n=1,b=true},
{n=2,b=false}).b
Neither type is betterSlide69
Sharing Bounded Type Variables
E
≤
lacks principal typesCan type variables for x and y be shared?If their separate bounds are compatible
X<:{n:Int}
and
Y<:{}
can be combined to
{n:Int}
X<:{n:Int}
and
Y<:{n:Bool}
cannot be combined
69Slide70
E≤ Inference
Strategy
If possible, use same type variable
All call sites well-typedOtherwise, restart and keep vars separate
Otherwise, use separate variables
Keeping separate variables is just like System E
Now there is a second cause for restarting
70Slide71
E≤ Inference with Run-time Logs
Know when to share from run-time info?
Can’t determine if all call sites well-typed
even if actual has a field, its static type might notCan determine if some call site is notif an actual doesn’t have field, static type will notSo can eliminate some iteration but not all
71Slide72
Summary
bounded quantification
72
subtyping
+
polymorphism
System E
≤
System E
System E
−Slide73
Summary
Iterative static inference for E
≤
first-order functions, recordspolymorphism, subtypingbounded quantificationRun-time information improves algorithmsreduces worst-case complexity(not counting overhead for instrumented eval)
73Slide74
Future Work
Direction #0: Prove formal properties for E
≤
Direction #1: Extend E≤recursion, recursive types, dynamic, classes, ...is there still static inference?how can run-time info help?can existing programs be encoded in E?Direction #2: Inference for F
does inference become easier with run-time info?
if so, extend with more features
if not, heuristic-based approaches for migration
74Slide75
Related Work
Complete inference for ML + records
[Remy 89]
limited by ML polymorphismand fields cannot be forgottenType inference for F is undecidable [Wells 94]Local type inference for F [Pierce et al, Odersky et al, et al]require top-level annotations, try to fill in resteven partial inference for F undecidable
[Pfenning 88]
Type checking for F
≤
is undecidable
[Pierce 91]
75Slide76
Related Work
Static type systems for dynamic languages
type systems for JavaScript
[Thiemann 05, et al]Typed Scheme [Tobin-Hochstadt and Felleisen 08]Diamondback Ruby [Furr et al 09 11]Gradual type systemsfunctions [Thatte 90, Cartwright and Fagan 91, Siek and Taha 06]objects
[Siek and Taha 07, Wrigstad et al 10, Bierman et al 10]
Coercion insertion
[Henglein/Rehof 95, Siek/Vachharajani 08]
do not deal with records
76Slide77
Thanks!
Released under
Creative Commons Attribution 2.0 Generic License
77
Original photo
by Alaskan
DudeSlide78
Extra Slides
78Slide79
Programs
Program is a sequence of function definitions
def y
1[A1,...,An
] (x
1
:τ
1
) { e
1
}
def y
2
[B
1
,...,B
m
] (x
2
:τ
2
) { e
2 } def main[] (z:Unit) { e3 }
Untyped definitions def y1
(x1) { e1’ }79Slide80
Programs
Expressions
e ::=
| n | b | e1 + e2
| not e | ...
| {f
i
=e
i
} | e.f
| y[τ
1
,...,τ
n
](e)
| if e
1
then e
2
else e
3
| let x = e
1 in e2
Untyped function calls y(e’)
80Slide81
τ ::=
| Int | Bool | ...
| {f
i:τi} | {fi
:τ
i
}
y.l
| {f
i
:τ
i
}
k.l
| X
y.l
| X
k.l
81
“this record type came from if-expression
k
and
then projected on sequence of fields
l
”
New Annotated Types for E
“this type variable is for if-expression
k
and then projected on sequence of fields
l
”