1 / 82

Nested Refinement Types for Dynamic Languages

Nested Refinement Types for Dynamic Languages. Ravi Chugh. What are “Dynamic” Languages?. Untyped, and characterized by common features. Reflective type-tests typeof x == ‘number’ Dictionary-style objects obj [ key ] = val “Duck typing” if ( duck.quack ) { duck.quack () }

shepry
Download Presentation

Nested Refinement Types for Dynamic Languages

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. Nested Refinement Typesfor Dynamic Languages Ravi Chugh

  2. What are “Dynamic” Languages? Untyped, and characterized by common features • Reflective type-tests typeof x == ‘number’ • Dictionary-style objects obj[key] = val • “Duck typing” if(duck.quack){ duck.quack()} • Dynamic code generation eval(‘var x = 1’) • Rich string primitives arr.join(‘,’)

  3. Why Study Dynamic Languages? http://stackoverflow.com/tags 11-11-11

  4. Why Study Dynamic Languages? Pervasive, thanks to the web • No static checking  rapid prototyping • String libraries + code genmulti-language systems Important, thanks to the web • Large applications written in these languages • Reliability matters: security, availability, extensibility • Performance matters: the new Browser Wars • No static checking  reliability and performance is hard JavaScript is at the heart of this

  5. Isn’t JavaScript a Terrible Language? undefined value scope manipulation primitive typemanipulation implicit updates toglobal object {} typeofx := 1super implicit coercion misleading syntax eval() weird comparasions NaN != NaN Infinity == Infinity ‘,,,’ == new Array(4) implicitvar lifting

  6. Isn’t JavaScript Awesome?! Core consists of bread-and-butter features {} typeofx := 1super These features not going away, nor should they* Found in many languages, typed and untyped Lambdas in a mainstream language! • Core features are difficult  good for research • Core features are expressive  good for practice JavaScript is evolving

  7. Research Plan System D lambdas, dictionaries, tag-tests + explicit references + prototype-based inheritance Dependent JavaScript translates to D++ Applications in DJS DJS {} typeofx := 1super + prototypes + references D

  8. Research Plan System D lambdas, dictionaries, tag-tests + explicit references + prototype-based inheritance Dependent JavaScript translates to D++ Applications in DJS DJS {} typeofx := 1super + prototypes + references D

  9. Research Plan System D lambdas, dictionaries, tag-tests + explicit references + prototype-based inheritance Dependent JavaScript translates to D++ Applications in DJS DJS {} typeofx := 1super + prototypes + references D

  10. Motivating Example tag-tests affect control flow iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0) dictionaries indexed by string literals or arbitrary values first-class functions can appear inside dictionaries

  11. Approach: Refinement Types tag-tests Type environment tracks control flow predicates x :: {|tag()=“Int”tag()=“Bool”} x :: {|tag()=“Bool”} x :: {|tag()=“Int”} iftagofx=“Int”then 0 - xelse notx

  12. Approach: Refinement Types tag-tests d.n + d[m] dictionaries {|tag()=“Dict” tag(sel(,“n”))=“Int” tag(sel(,m))=“Int”} d :: McCarthy axioms

  13. Approach: Refinement Types tag-tests 1 +f(0) dictionaries first-class functions x.x :: f :: x:{|tag()=“Int”}{|tag()=“Int”} x:{|tag()=“Int”}{|n=x} Clear separation of base and arrow types T ::= {|p} | x:T1T2

  14. Approach: Refinement Types tag-tests 1 +f(0) dictionaries first-class functions

  15. Approach: Refinement Types tag-tests 1 + d[f](0) dictionaries d :: {|tag()=“Dict”  sel(,f) ??? } first-class functions Key Challenges How to describe arrow inside formula? How to keep type checking decidable?

  16. Approach • Reuse refinement type architecture • Find a decidable refinement logic for • Tag-tests • Dictionaries • Lambdas • Define nested refinement type architecture ✓ ✓ ✗ ✓*

  17. Nested Refinements 1 + d[f](0) • {|tag()=“Dict” • sel(,f):: • } d :: {|tag()=“Int”}  {|tag()=“Int”} uninterpreted predicate“x::U” says“x has-type U” uninterpreted constantin the logic… … but syntactic arrowin the type system!

  18. Nested Refinements • Refinement formulas over a decidable logic • uninterpreted functions, McCarthy arrays, linear arithmetic • Only base values refined by formulas All values T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | …| x :: U T ::= {|p} | x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … traditional refinements

  19. Nested Refinements • Refinement formulas over a decidable logic • uninterpreted functions, McCarthy arrays, linear arithmetic • Only base values refined by formulas • “has-type” allows “type terms” in formulas All values T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U T ::= {|p} | x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … traditional refinements

  20. Nested Refinements • Refinement formulas over a decidable logic • uninterpreted functions, McCarthy arrays, linear arithmetic • Only base values refined by formulas • “has-type” allows “type terms” in formulas All values T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

  21. let foofd = iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0) f:{|Str()::IntInt} d:{|Dict() Int(.n) Str(f)[f]::IntInt} Int foo :: T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

  22. let foofd = iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0) f:{|Str()::IntInt} d:{|Dict() Int(.n) Str(f)[f]::IntInt} Int foo :: T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

  23. let foofd = iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0)  {|tag()=“Int”} {|tag()=“Int”} f:{|Str()::IntInt} d:{|Dict() Int(.n) Str(f)[f]::IntInt} Int foo :: tag()=“Str” T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

  24. let foofd = iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0) f:{|Str()::IntInt} d:{|Dict() Int(.n) Str(f)[f]::IntInt} Int foo :: T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

  25. let foofd = iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0) sel(n,“n”) sel(n,f) f:{|Str()::IntInt} d:{|Dict() Int(.n) Str(f)[f]::IntInt} Int foo :: T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

  26. let foofd = iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0) f:{|Str()::IntInt} d:{|Dict() Int(.n) Str(f)[f]::IntInt} Int foo :: T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

  27. let foofd = iftagoff=“Str”thend.n + d[f](0) elsed.n + f(0) foo :: f: {|tag()=“Str”::} IntInt  d: {|tag()=“Dict” tag(sel(,“n”))=“Int” tag(f)=“Str” sel(,f):: } {|:: {|:: IntInt T ::= {|p}U ::= x:T1T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U Int  } }

  28. Nested Refinements • Type Language • Subtyping • Extensions • Recap

  29. Subtyping T ::= {|p} | x:T1T2 Subtyping Top Int {|true} {|tag()=“Int”} traditional refinements tag()=“Int”true Implication Int <: Top SMT Solver

  30. Subtyping T ::= {|p} | x:T1T2 Subtyping Top Int {|true} {|tag()=“Int”} traditional refinements tag()=“Int” true tag()=“Int” tag()=“Int” Implication SMT Solver Int <: Top Int <: Int Top  Int <: Int  Int

  31. Subtyping T ::= {|p} | x:T1T2 Subtyping Arrow Rule Top Int {|true} {|tag()=“Int”} traditional refinements tag()=“Int” true tag()=“Int” tag()=“Int” Implication SMT Solver Int <: Top Int <: Int Top  Int <: Int  Int

  32. Subtyping T ::= {|p} | x:T1T2 Subtyping Arrow Rule traditional refinements • Decidable if: • Only values in formulas • Underlying theories decidable Implication SMT Solver • With nested refinements: • No new theories • But implication is imprecise!

  33. Subtyping with Nesting Subtyping Implication ✗ ::TopInt::IntInt SMT Solver Invalid, as these are distinct uninterpreted constants

  34. Subtyping with Nesting When goal is base predicate:pq When goal is “has-type” predicate:px::U Subtyping Subtyping Arrow Rule Implication Implication U’<:U px::U’ pq SMT Solver SMT Solver

  35. Subtyping with Nesting UninterpretedReasoning Normalize formulas to subdivide obligations appropriately SyntacticReasoning + p ::TopInt TopInt<:IntInt p::Int  Int

  36. Nested Refinements • Type Language • Subtyping • Extensions • Recap

  37. Extensions • Simple to add additional type constructors • Extend the grammar of type terms • Add additional syntactic subtyping rules U ::= x:T1T2 | A | List[T] | Null SyntacticRules Arrow Rule Covariant List Rule Null List Rule

  38. Map ∀A,B. {|::AB}{|::List[A]}{|::List[B]} let mapfxs = ifxs=nullthen null else f xs[“hd”] :: map f xs[“tl”] encode recursive data as dictionaries

  39. Filter let filterfxs = ifxs=nullthen null else if f xs[“hd”]then xs[“hd”] :: filter f xs[“tl”] else filter f xs[“tl”] • ∀A,B.{|::x:A{|n=Truex::B}} • {|::List[A]} • {|::List[B]} usual definition, but an interesting type

  40. Dispatch let dispatch d f=d[f] d • ∀A,B.d:{|Dict() ::A} • {|Str() d[]::AB} • {|::B} a form of “bounded quantification” since d::A but additional constraints on A

  41. Recap • Refinement types are a compelling approach • Dynamic dictionaries require dependency • Tag-tests require path sensitivity • But, not enough for lambdas in dictionaries • Nested refinement types are a clean solution • Natural way to describe dynamic idioms • Novel subtyping remains decidable and automatic • Interesting soundness proof

  42. Research Plan System D lambdas, dictionaries, tag-tests + explicit references + prototype-based inheritance Dependent JavaScript translates to D++ Applications in DJS DJS {} typeofx := 1super + prototypes + references D

  43. Research Plan System D lambdas, dictionaries, tag-tests + explicit references + prototype-based inheritance Dependent JavaScript translates to D++ Applications in DJS DJS {} typeofx := 1super + prototypes + references D

  44. System D functionalupdate let x = {} in let x’ = x with “f” = 1 in x’.f JavaScript var x = {} x.f= 1 x.f x’ :: x :: {|=upd(x,“f”,1)} {|=empty} mutation!

  45. System !D = D + Explicit References Update to reference cell doesn’t affect type So this dictionary read does not type check! JavaScript System !D var x = {} x.f= 1 x.f let x = ref {} in let _ = x := (!x with “f” = 1)in let _ = (!x)[“f”] x stores value of type {|Dict()} need to strongly update reference type

  46. System !D = D + Explicit References • In JS, every dictionary is stored in a reference • No strong update cannot extend dictionary! • So, we must support flow-sensitive invariants var x = {} x.f= 1 x.f let x = ref {} in let _ = x := (!x with “f” = 1)in let _ = (!x)[“f”]

  47. Strong Update à la Alias Types • Types are flow-insensitive (as usual) • But heap types are flow-sensitive • Mappings from locations to types can change U ::= … | RefL let x = ref {} in let _ = x := (!x with “f” = 1)in let _ = (!x)[“f”] x :: {|::RefL} safe given the updated heap type L::d: {|=empty} L::d’: {|=upd(d,“f”,1)}

  48. One Last Feature: Prototypes • Each object is backed by a prototype object • Can be any ordinary object • Immutable link set at construction time* • Object read x[k] • If x has key k, return the binding • Else, recurse on x.__proto__[k] • Object write x[k]=y • does not affect prototype chain

  49. var grandpa = { “surname” = “Quackenboss”, “saying” = “When I was your age…”, “age” = 77 } var parent = { “age” = 44 } var child = { “age” = 22 } var a = child.age Assume proto chain is: 1. does childhave age? 2. what is type of child.age? grandpa a :: a :: a :: {| = 22  = 44  = 77} {| = 22} {|Int()} parent child

  50. System !D += Prototypes L3:: dGrandpa: L2:: dParent: L1:: dChild: Unknown Part of Heap • Track prototype links in heap type • immutable and acyclic • New uninterpreted predicate ObjHas(H,L,k) • analog to has(d,k) (macro forsel(d,k) != bot ) • New uninterpreted function ObjSel(H,L,k) • analog to sel(d,k) H T3 T2 T1 grandpa :: parent :: child :: {|::RefL3} {|::RefL2} {|::RefL1}

More Related