1 / 81

Type Inference with Run-time Logs

Type Inference with Run-time Logs. Ravi Chugh. Motivation: Dynamic Languages. Dynamically-typed languages Enable rapid prototyping Facilitate inter-language development Statically-typed languages Prevent certain run-time errors Enable optimized execution Provide checked documentation

Download Presentation

Type Inference with Run-time Logs

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Type Inferencewith Run-time Logs Ravi Chugh

  2. Motivation: Dynamic Languages • Dynamically-typed languages • Enable rapid prototyping • Facilitate inter-language development • Statically-typed languages • Prevent certain run-time errors • Enable optimized execution • Provide checked documentation • Many research efforts to combine both • Recent popularity of Python, Ruby, and JavaScript has stoked the fire

  3. Static Type Systems • Many attempts at fully static type systems • Flow sensitive types, occurrence types, ... • Often require refactoring and annotation • Some features just cannot be statically typed • Retrofitting existing language unlikely • Could work for a new language • But we want to migrate existing programs

  4. Gradual Type Systems • Typing is not all-or-nothing • Some expressions have types, others are dynamic • Typed portions checked in standard ways • Untyped portions fall back on run-time checks • Challenges • Granularity • Guarantees • Blame tracking • and ...

  5. ... Inference! • Goal: migrate programs from dynamic langs • Programmer annotation burden is currently 0 • A migration strategy must require ~0 work • Even modifying 1-10% LOC in a large codebasecan be prohibitive

  6. The Challenge: Inference Goal: practical type system polymorphism def id(x) { return x }; 1 + id(2); “hello” ^ id(“world”); id : ∀X. X → X polymorphism

  7. The Challenge: Inference Goal: practical type system subtyping def succA(x) { return (1 + x.a)}; succA({a=1}); succA({a=1;b=“hi”}); subtyping polymorphism {a:Int;b:Str} <: {a:Int}

  8. The Challenge: Inference Goal: practical type system bounded quantification def incA(x) { x.a := 1 + x.a; return x}; incA({a=1}).a; incA({a=1;b=“hi”}).b; bounded quantification subtyping polymorphism incA:∀X<:{a:Int}.X→X

  9. The Challenge: Inference Goal: practical type system dynamic n := if b then 0 else “bad”; m := if b then n + 1 else 0; n : dynamic bounded quantification subtyping polymorphism dynamic

  10. The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism dynamic other features...

  11. The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism dynamic other features... System ML ✓

  12. The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism dynamic other features... System F ✗

  13. The Idea • Use run-time executions to help inference • Program may have many valid types • But particular execution might rule out some • Dynamic language programmers test a lot • Use existing test suites to help migration

  14. Route: Inference w/ Run-time Logs omit higher-order functions System E≤ System E bounded quantification subtyping + polymorphism

  15. First Stop omit higher-order functions System E≤ System E bounded quantification subtyping System E− + polymorphism

  16. Typed vs. Untyped Syntax def y[A1,...,An](x:τ){ e } def y(x){ e’ } • y[τ1,...,τn](e) y(e’) Function definitions Function calls Program is sequence of function definitions

  17. E− Type System Expression and function types τ ::= | Int | Bool | ... | {fi:τi} | X σ ::= ∀Xi. τ1 → τ2 Typing rules prevent field-not-found and primitive operation errors No rule for if-expressions yet

  18. 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...

  19. ∀X. X → X • Int → Int • ∀Y,Z. Y*Z → Y*Z ... but ∀X. X → X is the principal type

  20. ∀X. X → X • Int → Int • ∀Y,Z. Y*Z → Y*Z • ∀X. X*X → X*X • Int*Bool → Int*Bool • Int*Int → Int*Int ... but ∀X. X → X is the principal type More general than every other type Allows id to be used in most different ways

  21. ∀A. {a:A} → A • {a:Int} → Int • ∀A,B. {a:A;b:B} → A • ∀B. {a:Int;b:B} → Int def readA (x) { x.a } def readA[A] (x:{a:A}) { x.a } : ∀A. {a:A} → A This is the best type

  22. ∀A.{a:A}→{a:A} • ∀A,B.{a:A;b:B}→{a:A;b:B} • allows • foo({a=1}) • allows • foo({a=1;b=2}).b def foo (o) { let _ = o.a in o } Two valid types: Neither is better than the other

  23. E− Static Type Inference • E− lacks principal types • Cannot assign type just from definition • Need to consider calling contexts • Our approach is iterative • Impose minimal constraints on argument • If a calling context requires an additional field to be tracked, backtrack and redo the function

  24. Annotated Types τ ::= | Int | Bool | ... | {fi:τi} | X

  25. Annotated Types • “this type variable is for parameter of y and then projected on sequence of fields l” τ ::= | Int | Bool | ... | {fi:τi} | Xy.l

  26. Annotated Types • def id (x) { x } • ∀X. X → X • ∀Xid. Xid→ Xid τ ::= | Int | Bool | ... | {fi:τi} | Xy.l

  27. Annotated Types • def readA (x) { x.a } ∀A. {a:A} → A • ∀XreadA.a. {a:XreadA.a} → XreadA.a τ ::= | Int | Bool | ... | {fi:τi} | Xy.l

  28. Annotated Types • def readAB (x) { x.a.b } ∀AB. {a:{b:AB}} → AB • ∀XreadA.a.b. {a:{b:XreadA.a.b}} → XreadA.a.b τ ::= | Int | Bool | ... | {fi:τi} | Xy.l

  29. Annotated Types τ ::= | Int | Bool | ... | {fi:τi} | Xy.l

  30. Annotated Types • “this record type came from parameter of y andthen projected on sequence of fields l” τ ::= | Int | Bool | ... | {fi:τi} | {fi:τi}y.l | Xy.l

  31. Annotated Types τ ::= | Int | Bool | ... | {fi:τi} | {fi:τi}y.l | Xy.l • def foo (o) { let _ = o.a in o } ∀A. {a:A} → {a:A} ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo

  32. Annotated Types τ ::= | Int | Bool | ... | {fi:τi} | {fi:τi}y.l | Xy.l • def foo (o) { let _ = o.a in o } ∀A,B. {a:A;b:B} → {a:A;b:B} ∀Xfoo.a,Xfoo.b. {a:Xfoo.a;b:Xfoo.b} → {a:Xfoo.a;b:Xfoo.b}foo

  33. Iterative Inference – Example 1 def foo (o) { let _ = o.a in o } def main () { foo({a=1}) } foo : ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo Processing foo... • Xfoo <: {a : Xfoo.a} Iteration 0

  34. Iterative Inference – Example 1 def foo (o) { let _ = o.a in o } def main () { foo({a=1}) } foo : ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo Processing main... • {a=1} : {a:Int} ✓ • {a:Int} ≤ {a:Xfoo.a} ? Iteration 0

  35. Iterative Inference – Example 2 def foo (o) { let _ = o.a in o } def main () { let z = {a=1;b=2} in foo(z).b } foo : ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo Processing foo... • Xfoo <: {a : Xfoo.a} Iteration 0

  36. Iterative Inference – Example 2 def foo (o) { let _ = o.a in o } def main () { let z = {a=1;b=2} in foo(z).b } foo : ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo Processing main... • z : {a:Int;b:Int} ✓ • {a:Int;b:Int} ≤ {a:Xfoo.a} ? Iteration 0

  37. Iterative Inference – Example 2 def foo (o) { let _ = o.a in o } def main () { let z = {a=1;b=2} in foo(z).b } Caller-induced constraints • Xfoo <: {b : Xfoo.b} foo : ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo Processing main... • z : {a:Int;b:Int} • {a:Int;b:Int} ≤ {a:Xfoo.a} ? • foo(z) : {a:Int}foo ✗* ✗ • {a:Int}foo ≤ {b:X} ? Iteration 0

  38. Iterative Inference – Example 2 def foo (o) { let _ = o.a in o } def main () { let z = {a=1;b=2} in foo(z).b } Caller-induced constraints • Xfoo <: {b : Xfoo.b} foo : ∀Xfoo.a. {a:Xfoo.a;b:Xfoo.b} → {a:Xfoo.a;b:Xfoo.b}foo Processing foo... • Xfoo <: {a : Xfoo.a} Iteration 1

  39. Iterative Inference – Example 2 def foo (o) { let _ = o.a in o } def main () { let z = {a=1;b=2} in foo(z).b } Caller-induced constraints • Xfoo <: {b : Xfoo.b} foo : ∀Xfoo.a. {a:Xfoo.a;b:Xfoo.b} → {a:Xfoo.a;b:Xfoo.b}foo Processing main... • z : {a:Int;b:Int} ✓ • {a:Int;b:Int} ≤ {a:Xfoo.a;b:Xfoo.b} ? Iteration 1

  40. Iterative Inference – Example 2 def foo (o) { let _ = o.a in o } def main () { let z = {a=1;b=2} in foo(z).b } Caller-induced constraints • Xfoo <: {b : Xfoo.b} foo : ∀Xfoo.a. {a:Xfoo.a;b:Xfoo.b} → {a:Xfoo.a;b:Xfoo.b}foo Processing main... • z : {a:Int;b:Int} • {a:Int;b:Int} ≤ {a:Xfoo.a;b:Xfoo.b} ? • foo(z) : {a:Int;b:Int}foo ✓ • {a:Int;b:Int}foo ≤ {b:X} ? Iteration 1

  41. Iterative Inference – Example 3 ✓ def foo (o) { let _ = o.a in o } def main () { let _ = foo({a=1}) in let z = {a=1;b=2} in foo(z).b } ✓ foo : ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo Iteration 0

  42. Iterative Inference – Example 3 def foo (o) { let _ = o.a in o } def main () { let _ = foo({a=1}) in let z = {a=1;b=2} in foo(z).b } ✗* foo : ∀Xfoo.a. {a:Xfoo.a} → {a:Xfoo.a}foo Iteration 0

  43. Iterative Inference – Example 3 ✗ def foo (o) { let _ = o.a in o } def main () { let _ = foo({a=1}) in let z = {a=1;b=2} in foo(z).b } foo : ∀Xfoo.a. {a:Xfoo.a;b:Xfoo.b} → {a:Xfoo.a;b:Xfoo.b}foo • Calling contexts impose incompatibleconstraints on type of foo Iteration 1

  44. E− Static Type Inference In iteration 0, don’t have calling context info Iteratively constraint Xfoo as needed Process foo and its callers again No principal types, but still static inference Can run-time information improve algorithm?

  45. 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 Xy tag • When a value with tag Xy.lprojected on field f, record Xy.l <: {f : Xy.l.f} • Iteration 0 of inference looks in log forcaller-induced constraints

  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},{Xfoo}]).b ⇒ (let _ = [{a=1;b=2},{Xfoo}].a in [{a=1;b=2},{Xfoo}]).b ⇒ (let _ = [1,{Xfoo.a}] in [{a=1;b=2},{Xfoo}]).b ⇒ [{a=1;b=2},{Xfoo}].b ⇒ [2,{Xfoo.b}] Run-time log • Xfoo <: {a : Xfoo.a} • Caller-inducedconstraint • Xfoo <: {b : Xfoo.b} Evaluation

  47. System E− Summary • 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 iteration

  48. Next Stop System E≤ System E System E bounded quantification subtyping System E− + polymorphism

  49. E Type System • if b then 1 else 2 • : Int ✗ • if b then 1 else true • if b then {f=1; g=“”} • else {f=2; h=true} • : {f:Int} • if b then {f=“”} • else {f=true} ✗ Type of if-expression is join of branch types

  50. shorthand for def bar (o) { if1 o.1.n > 0 then o.1 else o.2 } • τ1*τ2 shorthand for {1:τ1;2:τ2} def bar (x,y) { if1 x.n > 0 then x else y }

More Related