82
Nested Refinement Types for Dynamic Languages Ravi Chugh

Nested Refinement Types for Dynamic Languages Ravi Chugh

Embed Size (px)

Citation preview

Page 1: Nested Refinement Types for Dynamic Languages Ravi Chugh

Nested Refinement Typesfor Dynamic Languages

Ravi Chugh

Page 2: Nested Refinement Types for 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(‘,’)

Page 3: Nested Refinement Types for Dynamic Languages Ravi Chugh

3

Why Study Dynamic Languages?

http://stackoverflow.com/tags11-11-11

Count Tag235,334 C#175,865 Java147,557 JavaScript

95,609 C++80,941 Python44,447 C33,733 Ruby

4,652 Haskell1,265 Scheme

691 OCaml

Page 4: Nested Refinement Types for Dynamic Languages Ravi Chugh

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

Page 5: Nested Refinement Types for Dynamic Languages Ravi Chugh

5

Isn’t JavaScript a Terrible Language?

{}

typeofx := 1super

scope manipulation

weird comparasionsNaN != NaN

Infinity == Infinity ‘,,,’ == new Array(4)

implicit updates toglobal object

primitive typemanipulation

misleading syntax

implicitvar lifting

undefined value

eval()

implicit coercion

Page 6: Nested Refinement Types for Dynamic Languages Ravi Chugh

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

Page 7: Nested Refinement Types for Dynamic Languages Ravi Chugh

7

Research Plan

+ prototypes+ references

DJS

{}typeofx := 1super

D

System D lambdas, dictionaries, tag-tests

+ explicit references

+ prototype-based inheritance

Dependent JavaScript translates to D++

Applications in DJS

Page 8: Nested Refinement Types for Dynamic Languages Ravi Chugh

8

Research Plan

+ prototypes+ references

DJS

{}typeofx := 1super

D

System D lambdas, dictionaries, tag-tests

+ explicit references

+ prototype-based inheritance

Dependent JavaScript translates to D++

Applications in DJS

1.

2.

3.

Page 9: Nested Refinement Types for Dynamic Languages Ravi Chugh

9

Research Plan

+ prototypes+ references

DJS

{}typeofx := 1super

D

System D lambdas, dictionaries, tag-tests

+ explicit references

+ prototype-based inheritance

Dependent JavaScript translates to D++

Applications in DJS

1.

2.

3.

Page 10: Nested Refinement Types for Dynamic Languages Ravi Chugh

10

Motivating Example

if tagof f = “Str”then d.n + d[f](0) else d.n + f(0)

indexed by string literals

can appear inside dictionaries

affect control flow

or arbitrary values

tag-tests

dictionaries

first-class functions

Page 11: Nested Refinement Types for Dynamic Languages Ravi Chugh

11

Approach: Refinement Types

if tagof x = “Int” then 0 - x else not x

tag-tests

Type environment tracks control flow predicates

{|tag()=“Int”tag()=“Bool”}x ::

{|tag()=“Bool”}x ::

{|tag()=“Int”}x ::

Page 12: Nested Refinement Types for Dynamic Languages Ravi Chugh

12

Approach: Refinement Types

d.n + d[m]

d,k,k’,x. kk’

sel(upd(d,k,x),k) = x

sel(upd(d,k,x),k’) = sel(d,k’)

sel(empty,k) = bot

McCarthy axioms

{|tag()=“Dict” tag(sel(,“n”))=“Int”tag(sel(,m))=“Int”}

d ::

dictionaries

tag-tests

Page 13: Nested Refinement Types for Dynamic Languages Ravi Chugh

13

Approach: Refinement Types

1 + f (0)

first-class functions

dictionaries

tag-tests

Clear separation of base and arrow typesT ::= {|p} | x:T1T2

x:{|tag()=“Int”}{|tag()=“Int”}f ::

x:{|tag()=“Int”}{|n=x}x.x ::

Page 14: Nested Refinement Types for Dynamic Languages Ravi Chugh

14

Approach: Refinement Types

1 + f (0)

first-class functions

dictionaries

tag-tests

Page 15: Nested Refinement Types for Dynamic Languages Ravi Chugh

15

Approach: Refinement Types

1 + d[f](0)

{|tag()=“Dict”

sel(,f) ??? }d ::

first-class functions

dictionaries

tag-tests

Key Challenges

1. How to describe arrow inside formula?

2. How to keep type checking decidable?

Page 16: Nested Refinement Types for Dynamic Languages Ravi Chugh

16

• Reuse refinement type architecture

• Find a decidable refinement logic for– Tag-tests– Dictionaries– Lambdas

• Define nested refinement type architecture

Approach

✗✓✓*

Page 17: Nested Refinement Types for Dynamic Languages Ravi Chugh

17

Nested Refinements

{|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!

1 + d[f](0)

Page 18: Nested Refinement Types for Dynamic Languages Ravi Chugh

18

Nested Refinements

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x :: U

• Refinement formulas over a decidable logic– uninterpreted functions, McCarthy arrays, linear arithmetic

• Only base values refined by formulasAll values

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | …

traditional refinements

Page 19: Nested Refinement Types for Dynamic Languages Ravi Chugh

19

Nested Refinements

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

• 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} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | …

traditional refinements

Page 20: Nested Refinement Types for Dynamic Languages Ravi Chugh

20

Nested Refinements

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

• 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

Page 21: Nested Refinement Types for Dynamic Languages Ravi Chugh

21

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

f:{|Str():: IntInt }

d:{|Dict()

Int(.n)

Str(f) [f] :: IntInt }

Int

foo ::

Page 22: Nested Refinement Types for Dynamic Languages Ravi Chugh

22

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

f:{|Str():: IntInt }

d:{|Dict()

Int(.n)

Str(f) [f] :: IntInt }

Int

foo ::

Page 23: Nested Refinement Types for Dynamic Languages Ravi Chugh

23

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

f:{|Str():: IntInt }

d:{|Dict()

Int(.n)

Str(f) [f] :: IntInt }

Int

foo ::

tag() = “Str”

{|tag()=“Int” }

{|tag()=“Int” }

Page 24: Nested Refinement Types for Dynamic Languages Ravi Chugh

24

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

f:{|Str():: IntInt }

d:{|Dict()

Int(.n)

Str(f) [f] :: IntInt }

Int

foo ::

Page 25: Nested Refinement Types for Dynamic Languages Ravi Chugh

25

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

f:{|Str():: IntInt }

d:{|Dict()

Int(.n)

Str(f) [f] :: IntInt }

Int

foo ::

sel(n,“n”) sel(n,f)

Page 26: Nested Refinement Types for Dynamic Languages Ravi Chugh

26

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

f:{|Str():: IntInt }

d:{|Dict()

Int(.n)

Str(f) [f] :: IntInt }

Int

foo ::

Page 27: Nested Refinement Types for Dynamic Languages Ravi Chugh

27

{|::

}

{|::

} Int

d: {|tag()=“Dict”

tag(sel(,“n”))=“Int”

tag(f)=“Str”

sel(,f) :: }IntInt

f:{|tag()=“Str”:: }IntInt

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

foo ::

Page 28: Nested Refinement Types for Dynamic Languages Ravi Chugh

28

Nested Refinements

• Type Language

• Subtyping

• Extensions

• Recap

Page 29: Nested Refinement Types for Dynamic Languages Ravi Chugh

29

Subtyping

T ::= {|p} | x:T1T2

traditional refinements

Implication

SMT Solver

Subtyping

{|true }Top

tag()=“Int” true

{|tag()=“Int” }Int

Int <: Top

Page 30: Nested Refinement Types for Dynamic Languages Ravi Chugh

30

Subtyping

T ::= {|p} | x:T1T2

traditional refinements

Implication

SMT Solver

Subtyping

{|true }Top

{|tag()=“Int” }Int

Top Int <: Int Int

Int <: Top Int <: Int

tag()=“Int” true

tag()=“Int” tag()=“Int”

Page 31: Nested Refinement Types for Dynamic Languages Ravi Chugh

31

Subtyping

T ::= {|p} | x:T1T2

traditional refinements

Implication

SMT Solver

Subtyping

{|true }Top

{|tag()=“Int” }Int

Top Int <: Int Int

Int <: Top Int <: Int

tag()=“Int” true

tag()=“Int” tag()=“Int”

Arrow Rule

Page 32: Nested Refinement Types for Dynamic Languages Ravi Chugh

32

Subtyping

T ::= {|p} | x:T1T2

traditional refinements

Implication

SMT Solver

Subtyping Arrow Rule

Decidable if:• Only values in formulas• Underlying theories decidable

With nested refinements:• No new theories• But implication is imprecise!

Page 33: Nested Refinement Types for Dynamic Languages Ravi Chugh

33

Subtyping with Nesting

Implication

SMT Solver

Subtyping

Invalid, as these are distinctuninterpreted constants

:: Top Int :: Int Int✗

Page 34: Nested Refinement Types for Dynamic Languages Ravi Chugh

34

Subtyping with Nesting

Implication

SMT Solver

Subtyping Arrow Rule

When goal is base predicate:p q

When goal is “has-type” predicate:p x :: U

Implication

SMT Solver

Subtyping

p x :: U’

U’ <: U

p q

Page 35: Nested Refinement Types for Dynamic Languages Ravi Chugh

35

p :: Top Int

UninterpretedReasoning

Subtyping with Nesting

p :: Int Int

Normalize formulas tosubdivide obligations appropriately

Top Int <: Int Int

+ SyntacticReasoning

Page 36: Nested Refinement Types for Dynamic Languages Ravi Chugh

36

Nested Refinements

• Type Language

• Subtyping

• Extensions

• Recap

Page 37: Nested Refinement Types for Dynamic Languages Ravi Chugh

37

Extensions• Simple to add additional type constructors

• Extend the grammar of type terms

• Add additional syntactic subtyping rules

Arrow Rule

Covariant List Rule

Null List Rule

SyntacticRules

U ::= x:T1T2 | A | List[T] | Null

Page 38: Nested Refinement Types for Dynamic Languages Ravi Chugh

38

Map

let map f xs = if xs = null then null else f xs[“hd”] :: map f xs[“tl”]

∀A,B.

{|:: AB } {|:: List[A] } {|:: List[B] }

encode recursive data as dictionaries

Page 39: Nested Refinement Types for Dynamic Languages Ravi Chugh

39

Filter

let filter f xs = if xs = null then 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

Page 40: Nested Refinement Types for Dynamic Languages Ravi Chugh

40

Dispatch

let dispatch d f = d[f] d

∀A,B. d:{|Dict() :: A } {|Str() d[] :: AB } {|:: B }

a form of “bounded quantification” sinced :: A but additional constraints on A

Page 41: Nested Refinement Types for Dynamic Languages Ravi Chugh

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

Page 42: Nested Refinement Types for Dynamic Languages Ravi Chugh

42

Research Plan

+ prototypes+ references

DJS

{}typeofx := 1super

D

System D lambdas, dictionaries, tag-tests

+ explicit references

+ prototype-based inheritance

Dependent JavaScript translates to D++

Applications in DJS

1.

2.

3.

Page 43: Nested Refinement Types for Dynamic Languages Ravi Chugh

43

Research Plan

+ prototypes+ references

DJS

{}typeofx := 1super

D

System D lambdas, dictionaries, tag-tests

+ explicit references

+ prototype-based inheritance

Dependent JavaScript translates to D++

Applications in DJS

1.

2.

3.

Page 44: Nested Refinement Types for Dynamic Languages Ravi Chugh

44

{|=upd(x,“f”,1)}x’ ::

let x = {} in

let x’ = x with “f” = 1 in

x’.f

System D

{|=empty }x ::

functional update

mutation!

JavaScriptvar x = {}

x.f = 1

x.f

Page 45: Nested Refinement Types for Dynamic Languages Ravi Chugh

45

var x = {}

x.f = 1

x.f

System !D = D + Explicit Referencesallocate cell ref xdereference !x

update cell x := y

let x = ref {} in

let _ = x := (!x with “f” = 1) in

let _ = (!x)[“f”]

System !DJavaScript

{|Dict() }x stores value of type

Update to reference cell doesn’t affect type

So this dictionary read does not type check!

need to strongly update reference type

Page 46: Nested Refinement Types for Dynamic Languages Ravi Chugh

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”]

Page 47: Nested Refinement Types for Dynamic Languages Ravi Chugh

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 ::= … | Ref L{|:: Ref L }x ::

L :: d:{|=empty }

L :: d’: {|=upd(d,“f”,1)}safe given the

updated heap type

let x = ref {} in

let _ = x := (!x with “f” = 1) in

let _ = (!x)[“f”]

Page 48: Nested Refinement Types for Dynamic Languages Ravi Chugh

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

Page 49: Nested Refinement Types for Dynamic Languages Ravi Chugh

49

var grandpa = { “surname” = “Quackenboss”, “saying” = “When I was your age…”, “age” = 77 }

var parent = { “age” = 44 }

var child = { “age” = 22 }

Assume proto chain is:

child

grandpa

parent

1. does child have age?

{| = 22 = 44 = 77}a ::

var a = child.age

{| = 22}a ::

{|Int()}a ::

2. what is type of child.age?

Page 50: Nested Refinement Types for Dynamic Languages Ravi Chugh

50

System !D += Prototypes L3 :: dGrandpa:

L2 :: dParent:

L1 :: dChild:

{|:: Ref L3 }grandpa ::

{|:: Ref L2 }parent ::

{|:: Ref L1 }child ::

Unknown Part of Heap

H T1

T3

T2

• Track prototype links in heap type– immutable and acyclic

• New uninterpreted predicate ObjHas(H,L,k)

– analog to has(d,k) (macro for sel(d,k) != bot )

• New uninterpreted function ObjSel(H,L,k)

– analog to sel(d,k)

Page 51: Nested Refinement Types for Dynamic Languages Ravi Chugh

51

System !D += Prototypes L3 :: dGrandpa:

L2 :: dParent:

L1 :: dChild:

{|:: Ref L3 }grandpa ::

{|:: Ref L2 }parent ::

{|:: Ref L1 }child ::

Unknown Part of Heap

H T1

T3

T2

ObjHas(H,L1,“age”)

Checking if child has “age”

ObjHas(H,L2,“age”)has(dChild,“age”)

ObjHas(H,L3,“age”)has(dParent,“age”)

has(dGrandpa,“age”) ObjHas( ,L4,“age”)UnknownHeap

unroll the known part of heap

Page 52: Nested Refinement Types for Dynamic Languages Ravi Chugh

52

System !D += Prototypes L3 :: dGrandpa:

L2 :: dParent:

L1 :: dChild:

{|:: Ref L3 }grandpa ::

{|:: Ref L2 }parent ::

{|:: Ref L1 }child ::

Unknown Part of Heap

H T1

T3

T2

a = ObjSel(H,L1,k)

var a = child[k]

!has(dChild,k) a = ObjHas(H,L2,k)

has(dChild,k) a = sel(dChild,k)

!has(dParent,k) a = ObjHas(H,L3,k)

has(dParent,k) a = sel(dParent,k)

has(dGrandpa,k) a = sel(dGranpda,k) true

unroll the known part of heap

Page 53: Nested Refinement Types for Dynamic Languages Ravi Chugh

53

Recap• System D is functional, but JS is imperative

• Mutation– Track heap flow-sensitively to allow strong updates– Precise dictionary types live on the heap

• Prototype-based inheritance– Model read semantics by traversing proto chain

• System !D = D + Refs + Protos

Page 54: Nested Refinement Types for Dynamic Languages Ravi Chugh

54

Dependent JavaScript

Lambda Calculus+ References + Prototypes

• Translation due to Guha et al.

• System !D is type system for LC + Refs + Protos

• Add !D types to source-level JS syntax

• Update translation with !D types

+ !D Types

JavaScript

+ !D Types

Page 55: Nested Refinement Types for Dynamic Languages Ravi Chugh

55

Research Plan

+ prototypes+ references

DJS

{}typeofx := 1super

D

System D lambdas, dictionaries, tag-tests

+ explicit references

+ prototype-based inheritance

Dependent JavaScript translates to D++

Applications in DJS

1.

2.

3.

Page 56: Nested Refinement Types for Dynamic Languages Ravi Chugh

56

Research Plan

+ prototypes+ references

DJS

{}typeofx := 1super

D

System D lambdas, dictionaries, tag-tests

+ explicit references

+ prototype-based inheritance

Dependent JavaScript translates to D++

Applications in DJS

1.

2.

3.

Page 57: Nested Refinement Types for Dynamic Languages Ravi Chugh

57

Applications• “JavaScript: The Good Parts”

– patterns identified as good practice• Microbenchmarks

– ~14 KLOC in SunSpider• Extensions and widgets

– secure subsets to allow 3rd party apps• Frameworks

– large development cost by few expert programmers• Type inference in Firefox JIT

– perhaps add slower, offline, more precise analysis

Page 58: Nested Refinement Types for Dynamic Languages Ravi Chugh

-------

POPL -----

ECOOP ---

ICFP -OOPSLA -

--

POPL -----

Ph.D. ---

2011

2012

2013

2. DJS = D + refs + protos

✓1. System D

3. Applications

Page 59: Nested Refinement Types for Dynamic Languages Ravi Chugh

59

Additional Directions• Better Inference

– Untyped programmers allergic to annotations – Perhaps run-time techniques

• Class-based inheritance

• Contracts– Defer obligations not discharged statically

• Study of aliasing in practice– How often is mutation “necessary”?– Pipe dream: mainstream language with explicit refs

Classes + D + Refs + Protos

Page 60: Nested Refinement Types for Dynamic Languages Ravi Chugh

60

Thanks!

Collaborators: Pat Rondon, Ranjit Jhala

Dhttp://cseweb.ucsd.edu/~rchugh/research/nested/

::

Page 61: Nested Refinement Types for Dynamic Languages Ravi Chugh

61

Extra Slides

Page 62: Nested Refinement Types for Dynamic Languages Ravi Chugh

62

Constants

tagof :: x:Top{| = tag(x)}

mem :: d:Dictk:Str {|Bool() = True has(d,k)}

get :: d:Dictk:{|Str() has(d,)}{| = sel(d,k)}

set :: d:Dictk:Strx:Top {| = upd(d,k,x)}

rem :: d:Dictk:Str{| = upd(d,k,bot)}

Page 63: Nested Refinement Types for Dynamic Languages Ravi Chugh

63

• Types

• Formulas

• Logical Values

Macros{|tag()=“Int” }

Int

x:T1T2 {|:: }x:T1T2

tag(x) = “Str”Str(x)

sel(n,“k”)x.k

sel(n,k)x[k]

sel(d,k) != bothas(d,k)

∀k’. k’ != k sel(d,k) != sel(d’,k)

EqMod(d,d’,k)

Page 64: Nested Refinement Types for Dynamic Languages Ravi Chugh

64

Onto• TODO

Page 65: Nested Refinement Types for Dynamic Languages Ravi Chugh

65

Related Work• TODO

Page 66: Nested Refinement Types for Dynamic Languages Ravi Chugh

66

Top Int<: Int Int

+ Syntactic Reasoning

:: Top Int :: Top Int

Uninterpreted Reasoning

Subtyping with Nesting

:: Top Int :: Int Int

Normalize formulas tosubdivide obligations appropriately

Page 67: Nested Refinement Types for Dynamic Languages Ravi Chugh

67

Classic to Nested Refinementsin Three Steps

Page 68: Nested Refinement Types for Dynamic Languages Ravi Chugh

68

Refinement TypesB ::= Int | Bool | …T ::= {:B|p} | x:T1T2

p ::= pq | … | x = y | x < y | …

{:Int|true}5 ::

{:Int|0 < n < 10}5 ::

{:Int| n = 10}5 ::

x:{:Int|true}{:Int|true}x.x ::

x:{:Int|true}{:Int|=x}x.x ::

Page 69: Nested Refinement Types for Dynamic Languages Ravi Chugh

69

Refinement TypesB ::= Int | Bool | …T ::= {:B|p} | x:T1T2

p ::= pq | … | x = y | x < y | …

Type checking is decidable if:• Underlying refinement logic is decidable• Only program values appear in formulas

Page 70: Nested Refinement Types for Dynamic Languages Ravi Chugh

70

Tag-testsB ::= Int | Bool | …T ::= {:B|p} | x:T1T2

p ::= pq | … | x = y | x < y | …

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | …

if tagof x = “Int” then 0 - x else not x

can’t pick a single base type{:???|???}x ::

{:Any|tag()=“Int”tag()=“Bool”}x ::

{:Any|tag()=“Int”tag()=“Bool”}x ::

Page 71: Nested Refinement Types for Dynamic Languages Ravi Chugh

71

Tag-tests

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | …

if tagof x = “Int” then 0 - x else not x

Type environment tracks control flow

predicates

{|tag()=“Int”tag()=“Bool”}x ::

{|tag()=“Bool”}x ::

{|tag()=“Int”}x ::

Page 72: Nested Refinement Types for Dynamic Languages Ravi Chugh

72

Tag-tests

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | …

Page 73: Nested Refinement Types for Dynamic Languages Ravi Chugh

73

Dictionaries

d.n + d[m]

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | …

d,k,k’,x. kk’

sel(upd(d,k,x),k) = x

sel(upd(d,k,x),k’) = sel(d,k’)

sel(empty,k) = bot

McCarthy axioms

{|tag()=“Dict” tag(sel(,“n”))=“Int”tag(sel(,m))=“Int”}

d ::

Page 74: Nested Refinement Types for Dynamic Languages Ravi Chugh

74

Dictionaries

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | …

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | …

d,k,k’,x. kk’

sel(upd(d,k,x),k) = x

sel(upd(d,k,x),k’) = sel(d,k’)

sel(empty,k) = bot

McCarthy axioms

Page 75: Nested Refinement Types for Dynamic Languages Ravi Chugh

75

Lambdas

T ::= {|p} | x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | …

1 + d[f](0)

How to describe arrow in a formula?

Key Challenge:

{|tag()=“Dict”sel(,f)) ??? }d ::

Page 76: Nested Refinement Types for Dynamic Languages Ravi Chugh

76

Refinement Types

if x = 1 then …else …

aka “subset types” and “contract types”

1,x=1

2,x1

x=1 on then-branch

x=2 on else-branch

Suppose

• Guard formulas stored in typing environment

• Logic: a natural way to track control flow!

B ::= Int | Bool | …T ::= {:B|p}

| x:T1T2

p ::= pq | … | x = y | x < y | …

x:{:Int|0<<3}

Page 77: Nested Refinement Types for Dynamic Languages Ravi Chugh

77

Type for foo, bottom-up

Page 78: Nested Refinement Types for Dynamic Languages Ravi Chugh

78

{|::

}

{|::

}

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

{|tag()=“Int” }Int

Int

d: {|tag()=“Dict”

tag(sel(,“n”))=“Int”

tag(f)=“Str”

sel(,f) :: }IntInt

f:{|tag()=“Str”:: }IntIntfoo ::

x:T1T2 {|:: }x:T1T2

Page 79: Nested Refinement Types for Dynamic Languages Ravi Chugh

79

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

{|tag()=“Int” }Int

x:T1T2 {|:: }x:T1T2

f:{|tag()=“Str”:: IntInt }

d:{|tag()=“Dict”

tag(sel(,“n”))=“Int”

tag(f)=“Str” sel(,f) :: IntInt }

Int

foo ::

tag(x)=“Str”Str(x)

Page 80: Nested Refinement Types for Dynamic Languages Ravi Chugh

80

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

{|tag()=“Int” }Int

x:T1T2 {|:: }x:T1T2

f:{|Str():: IntInt }

d:{|Dict()

Int(sel(,“n”))

Str(f) sel(,f) :: IntInt }

Int

foo ::

tag(x)=“Str”Str(x)

sel(n,“k”)x.k

sel(n,k)x[k]

Page 81: Nested Refinement Types for Dynamic Languages Ravi Chugh

81

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

T ::= {|p}U ::= x:T1T2

p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U

{|tag()=“Int” }Int

x:T1T2 {|:: }x:T1T2

f:{|Str():: IntInt }

d:{|Dict()

Int(.n)

Str(f) [f] :: IntInt }

Int

foo ::

tag(x)=“Str”Str(x)

sel(n,“k”)x.k

sel(n,k)x[k]

Page 82: Nested Refinement Types for Dynamic Languages Ravi Chugh

82

let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)

f:{|Str():: IntInt }

d:{|Dict() Int(.n) (Str(f) [f] :: IntInt) }

Int