/
Type Inference with Run-time Logs Type Inference with Run-time Logs

Type Inference with Run-time Logs - PowerPoint Presentation

ellena-manuel
ellena-manuel . @ellena-manuel
Follow
363 views
Uploaded On 2018-02-27

Type Inference with Run-time Logs - PPT Presentation

Ravi Chugh Motivation Dynamic Languages Dynamicallytyped languages Enable rapid prototyping Facilitate interlanguage development Staticallytyped languages Prevent certain runtime errors Enable optimized execution ID: 637989

bar foo def int foo bar int def type inference main system types constraints iteration bool induced time caller

Share:

Link:

Embed:

Download Presentation from below link

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.


Presentation Transcript

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