Top Banner
Nested Refinement Types for Dynamic Languages Ravi Chugh
82

Nested Refinement Types for Dynamic Languages

Jan 06, 2016

Download

Documents

SHEPRY

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 () } - PowerPoint PPT Presentation
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Nested Refinement Types for Dynamic Languages

Nested Refinement Typesfor Dynamic Languages

Ravi Chugh

Page 2: Nested Refinement Types for Dynamic Languages

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

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

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

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

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

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

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

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

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

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

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

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

14

Approach: Refinement Types

1 + f (0)

first-class functions

dictionaries

tag-tests

Page 15: Nested Refinement Types for Dynamic Languages

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

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

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

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

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

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

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

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

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

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

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

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

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

28

Nested Refinements

• Type Language

• Subtyping

• Extensions

• Recap

Page 29: Nested Refinement Types for Dynamic Languages

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

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

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

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

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

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

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

36

Nested Refinements

• Type Language

• Subtyping

• Extensions

• Recap

Page 37: Nested Refinement Types for Dynamic Languages

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-------

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

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

60

Thanks!

Collaborators: Pat Rondon, Ranjit Jhala

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

::

Page 61: Nested Refinement Types for Dynamic Languages

61

Extra Slides

Page 62: Nested Refinement Types for Dynamic Languages

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

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

64

Onto• TODO

Page 65: Nested Refinement Types for Dynamic Languages

65

Related Work• TODO

Page 66: Nested Refinement Types for Dynamic Languages

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

67

Classic to Nested Refinementsin Three Steps

Page 68: Nested Refinement Types for Dynamic Languages

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

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

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

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

72

Tag-tests

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

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

Page 73: Nested Refinement Types for Dynamic Languages

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

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

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

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

77

Type for foo, bottom-up

Page 78: Nested Refinement Types for Dynamic Languages

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

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

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

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

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