Slide 1

Slide 1 text

Towards Practical Gradual Typing Asumu Takikawa Daniel Feltey Earl Dean Matthew Flatt Robert Bruce Findler Sam Tobin-Hochstadt Matthias Felleisen ECOOP 2015, Prague

Slide 2

Slide 2 text

Gradual type systems ... “empower programmers so that they can gradually enrich scripts with types [...] as they perform maintenance work on the program. [Felleisen TLDI 2010] ”

Slide 3

Slide 3 text

PL Researcher Hat Soundness enables reliable checked documentation, optimization λ Maintenance Programmer Hat For real software need expressiveness and performance

Slide 4

Slide 4 text

Sound

Slide 5

Slide 5 text

Sound Practical

Slide 6

Slide 6 text

Sound Practical Expressive

Slide 7

Slide 7 text

Sound Practical Expressive Performant

Slide 8

Slide 8 text

Sound Practical Expressive Design + Eval Feedback Cycle Performant

Slide 9

Slide 9 text

Formal model [Takikawa et al. OOPSLA 2012] Implementation [Takikawa et al. ECOOP 2015] Our Story of Model → Impl. for Typed Racket & OOP

Slide 10

Slide 10 text

Formal model [Takikawa et al. OOPSLA 2012] Sound Implementation [Takikawa et al. ECOOP 2015] Expressive Performant Our Story of Model → Impl. for Typed Racket & OOP

Slide 11

Slide 11 text

Our three key contributions Propose a performance evaluation method for GT Expressiveness evaluation for our system Design lessons for OO gradual typing

Slide 12

Slide 12 text

Background on Typed Racket

Slide 13

Slide 13 text

Typed Racket implements a macro-level gradual type system which means we add types module-by-module = a module (untyped) = a program (4 modules)

Slide 14

Slide 14 text

Typed Racket implements a macro-level gradual type system which means we add types module-by-module #lang racket (define c% (class ....)) untyped

Slide 15

Slide 15 text

Typed Racket implements a macro-level gradual type system which means we add types module-by-module #lang typed/racket (: c% (Class [m (-> Int)])) (define c% (class ....)) typed

Slide 16

Slide 16 text

Modules agree to an interface at the boundary In Typed Racket it's expressed with types But interfaces also need to be enforced

Slide 17

Slide 17 text

integer? I expect an Integer Sound interaction requires compilation of types to contracts at module boundaries

Slide 18

Slide 18 text

string? I expect a String Sound interaction requires compilation of types to contracts at module boundaries

Slide 19

Slide 19 text

(object/c [m (-> integer? string?)]) I expect a (Object [m (-> Integer String)]) Sound interaction requires compilation of types to contracts at module boundaries

Slide 20

Slide 20 text

Higher-order contract requires a dynamic wrapper integer? string? Color on pipe corresponds to who is responsible for interface Method's input - typed Method's output - untyped

Slide 21

Slide 21 text

OO in Typed Racket

Slide 22

Slide 22 text

; Implements editing window in DrRacket (text:line-numbers-mixin (text:first-line-mixin (module-language-put-file-mixin (racket:text-mixin (color:text-mixin (drracket:bindings-keymap-mixin (mode:host-text-mixin (text:delegate-mixin (text:foreground-color-mixin (drracket:autocomplete-mixin (λ (x) x) (text:normalize-paste-mixin (text:column-guide-mixin text:info%)))))))))))) OOP in Racket uses mixins and �rst-class classes Mixins are functions on classes

Slide 23

Slide 23 text

; Implements editing window in DrRacket (text:line-numbers-mixin (text:first-line-mixin (module-language-put-file-mixin (racket:text-mixin (color:text-mixin (drracket:bindings-keymap-mixin (mode:host-text-mixin (text:delegate-mixin (text:foreground-color-mixin (drracket:autocomplete-mixin (λ (x) x) (text:normalize-paste-mixin (text:column-guide-mixin text:info%)))))))))))) OOP in Racket uses mixins and �rst-class classes Mixins are functions on classes

Slide 24

Slide 24 text

Mixins require structural subtyping and separate class types So in Typed Racket, types for classes/objects look like: (Class (init [enabled? Boolean]) (field [width Integer]) [get-width (-> Integer Integer)] (augment [get-width (-> Integer)])) (Object (field [width Integer]) [get-width (-> Integer Integer)]) Types are not just the names of classes

Slide 25

Slide 25 text

Background summary Typed Racket adds types module-by-module Dynamic checks, sometimes higher-order, added at boundaries Racket-style OOP requires structural types

Slide 26

Slide 26 text

Evaluation Criteria

Slide 27

Slide 27 text

How expressive is it? How performant is it?

Slide 28

Slide 28 text

How expressive is it? How much work is it to add type annotations? Does it support type reasoning in real programs? We tested it out by adding types to real untyped programs (e.g., GUI programs)

Slide 29

Slide 29 text

Performant - how fast is it? Credit: "White shark" by Terry Goss. Licensed under CC BY 2.5

Slide 30

Slide 30 text

How fast slow is it? Credit: "Manatee at Sea World Orlando Mar 10" by Ahodges7. Licensed under CC BY-SA 3.0.

Slide 31

Slide 31 text

GT suffers from cost of dynamic checks How slow does it get? In what situations are dynamic checks expensive?

Slide 32

Slide 32 text

GT suffers from cost of dynamic checks How slow does it get? In what situations are dynamic checks expensive?

Slide 33

Slide 33 text

GT suffers from cost of dynamic checks How slow does it get? In what situations are dynamic checks expensive?

Slide 34

Slide 34 text

Iterations through design/eval loop revealed several lessons Next section describes some of the lessons Evaluation Design feature Problem in adding types See if new features helps

Slide 35

Slide 35 text

Design Lessons

Slide 36

Slide 36 text

Design lesson = problem + solution The problems are condensed issues from real code bases

Slide 37

Slide 37 text

Problem 1 Structural types for the GUI “standard library” are complex

Slide 38

Slide 38 text

Classes can have complex interfaces Often mutually recursive and have lots of methods

Slide 39

Slide 39 text

Both self-recursion and mutual recursion Classes can have complex interfaces Often mutually recursive and have lots of methods

Slide 40

Slide 40 text

Both self-recursion and mutual recursion We need type syntax for both kinds of recursion Classes can have complex interfaces Often mutually recursive and have lots of methods

Slide 41

Slide 41 text

Class types are separate from class de�nitions So types duplicate code due to structural typing + inheritance

Slide 42

Slide 42 text

Class types are separate from class de�nitions So types duplicate code due to structural typing + inheritance ≥ 76 GUI classes in the real hierarchy

Slide 43

Slide 43 text

To reduce burden of writing types, Typed Racket has type aliases (define-type Path-String (U String Path)) We need our type aliases to do even more though

Slide 44

Slide 44 text

Solution part 1 General mutually recursive type aliases ; self recursion (define-type Button% (Class (init [callback (-> (Instance Button%) ...)]))) ; mutual recursion (define-type Bitmap% (Class [make-dc (-> (Instance Bitmap-DC%))] ...)) (define-type Bitmap-DC% (Class [get-bitmap (-> (U (Instance Bitmap%) #f))] ...))

Slide 45

Slide 45 text

Don't ordinary μ-types work? Recursive type aliases are similar to μ-types Far more convenient, especially for mutual recursion ; Mutual recursion with μ-types, it's a pain (define-type (T1 Y1 Y2) (U Null (Pairof String Y2))) (define-type (T2 Y1 Y2) (Pairof String Y1)) (define-type Even (μ Y1 (T1 Y1 (μ Y2 (T2 Y1 Y2))))) (define-type Odd (μ Y2 (T2 (μ Y1 (T1 Y1 Y2)) Y2)))

Slide 46

Slide 46 text

Solution part 2 Allow types to re�ect inheritance structure (define-type Text-Field% (Class [get-value (-> String)] [set-value (-> String Void)])) ; #:implements copies over get-value, set-value (define-type Combo-Field% (Class #:implements Text-Field% [append (-> String Void)]))

Slide 47

Slide 47 text

Problem 2 High overhead of higher-order object contracts

Slide 48

Slide 48 text

Performs tight loop Method impl Method call Returns same object Each boundary crossing adds a contract wrapper Can easily imagine at least linear growth

Slide 49

Slide 49 text

Performs tight loop Method impl Method call Returns same object Each boundary crossing adds a contract wrapper Can easily imagine at least linear growth

Slide 50

Slide 50 text

Performs tight loop Method impl Method call Returns same object Each boundary crossing adds a contract wrapper Can easily imagine at least linear growth

Slide 51

Slide 51 text

Performs tight loop Method impl Method call Returns same object Each boundary crossing adds a contract wrapper Can easily imagine at least linear growth

Slide 52

Slide 52 text

Performs tight loop Method impl Method call Returns same object Each boundary crossing adds a contract wrapper Can easily imagine at least linear growth

Slide 53

Slide 53 text

Performs tight loop Method impl Method call Returns same object Each boundary crossing adds a contract wrapper Can easily imagine at least linear growth

Slide 54

Slide 54 text

Performs tight loop Method impl Method call Returns same object Each boundary crossing adds a contract wrapper Can easily imagine at least linear growth

Slide 55

Slide 55 text

Solution Optimize unnecessary higher-order wrapping for objects Check if contracts are “stronger” than another Avoid a wrapper if the existing wrapper is stronger Inspired by space-ef�cient gradual typing [Herman et al. HOSC 2010] [Siek, Wadler POPL 2010] Not a new problem in theory. We found it occurs in real code

Slide 56

Slide 56 text

Design lessons Need a �exible type notation supporting recursion & inheritance Space-ef�ciency is needed, especially for object contracts

Slide 57

Slide 57 text

Detailed Evaluation

Slide 58

Slide 58 text

Left prong: how expressive is it? Added types to 12 Racket case studies ~5000 LOC + ~6000 LOC type defs All use OO features, some use mixins Mostly succeeded in accommodating OO patterns A thirteenth case failed (lacked bounded polymorphism)

Slide 59

Slide 59 text

Overall 15% increase for total lines of code Compared to 7% for functional code [Tobin-Hochstadt diss. 2010]

Slide 60

Slide 60 text

Overall 15% increase for total lines of code Compared to 7% for functional code [Tobin-Hochstadt diss. 2010] Bottom line: expressive enough, but do better on LOC

Slide 61

Slide 61 text

Right prong: how does it perform? How do we even evaluate that for GT? The state of the literature No comprehensive method so far Evaluation targeted at speci�c features or microbenchmarks

Slide 62

Slide 62 text

Insight Remember the gradual typing thesis from slide 1 empower programmers so that they can gradually enrich scripts with types [...] as they perform maintenance work on the program. Let's try to simulate this process on real programs Then see what the performance is like

Slide 63

Slide 63 text

Operative phrase: gradually enrich from untyped to typed Fully untyped

Slide 64

Slide 64 text

Operative phrase: gradually enrich from untyped to typed Fully untyped Type curious

Slide 65

Slide 65 text

Operative phrase: gradually enrich from untyped to typed Fully untyped Type curious Commi�ed to types

Slide 66

Slide 66 text

Operative phrase: gradually enrich from untyped to typed Fully untyped Type curious Commi�ed to types Fully typed

Slide 67

Slide 67 text

Operative phrase: gradually enrich from untyped to typed Fully untyped Fully typed

Slide 68

Slide 68 text

A �rst attempt: looked at lattices of 2 programs Are there “good” paths through lattice? Good as in minimal overhead or quick recovery Is fully typed performance ok or bad? Dynamic checks may remain in typed Are there pathological cases?

Slide 69

Slide 69 text

A �rst attempt: looked at lattices of 2 programs Are there “good” paths through lattice? Good as in minimal overhead or quick recovery Is fully typed performance ok or bad? Dynamic checks may remain in typed Are there pathological cases? Hypothesis No order-of-magnitude slowdown along any path

Slide 70

Slide 70 text

Acquire benchmark 39% 30% 38% 40% 38% 29% 31% 6% 33% 38% 40% 24% 5% 8% 35% 0% Percentage slowdown relative to untyped con�g Note bad typed performance (due to base libraries)

Slide 71

Slide 71 text

Go Fish benchmark 0% 1% 12% 32% 2% 12% 33% 1% 1% 12% 33% 1% 12% 32% 1% 0% Many paths have ok overhead at some point

Slide 72

Slide 72 text

We de�nitely had pathologies! Initial Acquire benchmark had exponential slowdown 2ⁿ wrappers for n crossings of typed/untyped boundary

Slide 73

Slide 73 text

We de�nitely had pathologies! Initial Acquire benchmark had exponential slowdown 2ⁿ wrappers for n crossings of typed/untyped boundary Just trying to make the lattice helped �nd this

Slide 74

Slide 74 text

What do evaluations tell us about future work? Annotation burden still too high More design work on notation, explore nominal Space ef�cient contracts important Current implementation is limited, should generalize Dynamic overhead too high JITing, nominal types, etc.

Slide 75

Slide 75 text

Conclusion

Slide 76

Slide 76 text

The Takeaway Evaluating performance of gradual typing is crucial We propose looking at lattice of type con�gurations Discover good/bad paths, pathological performance issues Gradual type systems should have a design/eval feedback cycle Model → Implementation → Evaluation → Model → ... http://docs.racket-lang.org/ts-guide/

Slide 77

Slide 77 text

The Takeaway Evaluating performance of gradual typing is crucial We propose looking at lattice of type con�gurations Discover good/bad paths, pathological performance issues Gradual type systems should have a design/eval feedback cycle Model → Implementation → Evaluation → Model → ... Thank you / Děkuji http://docs.racket-lang.org/ts-guide/