practical optional types for clojure - ambrose bs · 2020. 7. 14. · ambrose bonnaire-sergeant*...

90
Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure * Omnia Team, Commonwealth Bank of Australia**

Upload: others

Post on 21-Aug-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Ambrose Bonnaire-Sergeant* Rowan Davies**

Sam Tobin-Hochstadt*

Practical Optional Types for Clojure

Typed Clojure

*Omnia Team, Commonwealth Bank of Australia**

Page 2: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

I♥Clojure

Confession…

Page 3: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Why?

Lot of reasons…

I♥Clojure

Page 4: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Immutable data structures

Why I love Clojure!

Local reasoning

Hosted on JVM

Lisp-style Macros

Dynamic typing

Java

‘()

¯\_(ツ)_/¯

Page 5: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

I think …

Clojure encourages disciplined

programming

Page 6: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

How can we evaluate this

claim?

Question

Page 7: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Clojure

What does

look like?

But first…

Page 8: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

Global function

Immutable maps

Page 9: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

Immutable map literal

Immutable maps

Page 10: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

Prefix notation

Immutable maps

Page 11: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [1 2] {:x 1 :y 2})

(point 1 2) ;=> {:x 1 :y 2}

Prefix notation

Immutable maps

Page 12: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

(inc (:x (point 1 2))) ;=> 2

Keyword lookup

Immutable maps

Page 13: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn flip [n] (if (string? n) (- (Long/parseLong n)) (- n)))

(flip 42) ;=> -42

(flip “42”) ;=> -42

Local Reasoning

Page 14: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn flip [n] (if (string? n) (- (Long/parseLong n)) (- n)))

(flip 42) ;=> -42

(flip “42”) ;=> -42

Local Reasoning

Informs branches the type of n

Page 15: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn flip [42] (if (string? 42) (- (Long/parseLong n)) (- 42)))

(flip 42) ;=> -42

(flip “42”) ;=> -42

Local Reasoning

Takes ‘else’ branch

Page 16: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn flip [“42”] (if (string? “42”) (- (Long/parseLong “42”)) (- n)))

(flip 42) ;=> -42

(flip “42”) ;=> -42

Local Reasoning

Takes ‘then’ branch

Page 17: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn upper-case [s] (if s (.toUpperCase s) nil))

(upper-case nil) ;=> nil

(upper-case “abc”) ;=> “ABC”

Java interop Java

Test for nil (nil == null)

Page 18: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn upper-case [s] (if s (.toUpperCase s) nil))

(upper-case nil) ;=> nil

(upper-case “abc”) ;=> “ABC”

Java interop Java

Java instance method call

Page 19: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn upper-case [s] (if s (.toUpperCase s) nil))

(upper-case nil) ;=> nil

(upper-case “abc”) ;=> “ABC”

Java interop Java

Never nil, avoids NPE

Page 20: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point {:x 1 :y 2}) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Create new multimethod

Multimethods

Page 21: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point {:x 1 :y 2}) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Dispatch function

Multimethods

Page 22: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point {:x 1 :y 2}) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Install method

Multimethods

Page 23: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point {:x 1 :y 2}) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Dispatch value

Multimethods

Page 24: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point {:x 1 :y 2}) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Parameters

Multimethods

Page 25: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point {:x 1 :y 2}) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Method body

Multimethods

Page 26: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Run dispatch function 2. Use isa? to choose method 3. Dispatch and return result

Multimethods

(defmulti flip (fn [“4”] (class “4”)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip “4”) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Page 27: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [“4”] (class “4”)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip “4”) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Class literal i.e. String.class

Multimethods

Page 28: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [“4”] (class “4”)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip “4”) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

1. Run dispatch function 2. Use isa? to choose method 3. Dispatch and return result

(isa? (class “4”) String)

Multimethods

Page 29: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Run dispatch function 2. Use isa? to choose method 3. Dispatch and return result

(isa? (class “4”) String) ;=> true

Pick this method

Multimethods

(defmulti flip (fn [“4”] (class “4”)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip “4”) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Page 30: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Run dispatch function 2. Use isa? to choose method 3. Dispatch and return result

Multimethods

(defmulti flip (fn [“4”] (class “4”)))

(defmethod flip String [“4”] (- (Long/parseLong “4”)))

(defmethod flip Number [n] (- n))

(flip “4”) ;=> {:x -1 :y -2}

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

Page 31: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [“4”] (class “4”)))

(defmethod flip String [“4”] -4)

(defmethod flip Number [n] (- n))

(flip “4”) ;=> -4

(flip-point {:x “2” :y “1”}) ;=> {:x -2 :y -1}

1. Run dispatch function 2. Use isa? to choose method 3. Dispatch and return result

Multimethods

Page 32: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Understanding Clojure via Types

Page 33: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Understanding Clojure

?Method

Result

Goal

Understand Clojure better

Insights about Clojure

Page 34: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Understanding Clojure

Build and evaluate a type system for Clojure

Method

Result

Goal

Understand Clojure better

Insights about Clojure

Page 35: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure
Page 36: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Type system

Typed Clojure=

+Clojure

Page 37: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Building optional type systems

Page 38: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

Clojure Typed Clojure

Work

Page 39: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom2. Extend type

system

Clojure Typed Clojure

Work

Page 40: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom2. Extend type

system

1. Identify idiom2. Extend type

system

1. Identify idiom2. Extend type

system... ...Work

Clojure Typed Clojure

Page 41: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

(ann point [Int Int -> ‘{:x Int :y Int}]) (defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

(inc (:x (point 1 2))) ;=> 2

Known keyword entries

Page 42: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann point [Int Int -> ‘{:x Int :y Int}]) (defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

(inc (:x (point 1 2))) ;=> 2

1. Identify idiom

Known keyword entries

Page 43: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

(ann point [Int Int -> ???]) (defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

(inc (:x (point 1 2))) ;=> 2

2. Extend type system

Known keyword entries

Page 44: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

(ann point [Int Int -> ‘{:x Int :y Int}]) (defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

(inc (:x (point 1 2))) ;=> 2

2. Extend type system

Our solution: HMap types

Known keyword entries

Page 45: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

(ann point [Int Int -> ‘{:x Int :y Int}]) (defn point [x y] {:x x :y y})

(point 1 2) ;=> {:x 1 :y 2}

(inc (:x (point 1 2))) ;=> 2

2. Extend type system

3. Evaluate

Evaluation 64% of HMap lookups resolve to known entries

Known keyword entries

Page 46: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann flip [(U Str Int) -> Int]) (defn flip [n] (if (string? n) (- (Long/parseLong n)) (- n)))

Type-based control flow

1. Identify idiom

Page 47: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann flip [(U Str Int) -> Int]) (defn flip [n] (if (string? n) (- (Long/parseLong n)) (- n))) Same name

but different type

1. Identify idiom

2. Extend type system

Str

Int

Type-based control flow

Page 48: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann flip [(U Str Int) -> Int]) (defn flip [n] (if (string? n) (- (Long/parseLong n)) (- n)))

1. Identify idiom

2. Extend type system

Str

Int

Solution: Implement occurrence typing[1][1] Tobin-Hochstadt and Felleisen (ICFP ’10)

Type-based control flow

Page 49: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn upper-case [s] (if s (.toUpperCase s) nil))

Preventing null-pointer exceptions

1. Identify idiom

Page 50: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str]) (defn upper-case [s] (if s (.toUpperCase s) nil))

Preventing null-pointer exceptions

2. Extend type system

1. Identify idiom

Explicit nil type

Page 51: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str]) (defn upper-case [s] (if s (.toUpperCase s) nil))

Preventing null-pointer exceptions

2. Extend type system

1. Identify idiom

Nonnilable references

Page 52: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str]) (defn upper-case [s] (if s (.toUpperCase s) nil))

Preventing null-pointer exceptions

2. Extend type system

1. Identify idiom

Equivalent to String in Java

Page 53: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str]) (defn upper-case [s] (if s (.toUpperCase s) nil))

Preventing null-pointer exceptions

2. Extend type system

1. Identify idiom

Encode nil as a false value

Page 54: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str]) (defn upper-case [s] (if s (.toUpperCase s) nil))

Preventing null-pointer exceptions

2. Extend type system

1. Identify idiom

Str✓(U nil Str)✓

Page 55: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str]) (defn upper-case [s] (if s (.toUpperCase s) nil))

Preventing null-pointer exceptions

1. Identify idiom

Evaluation 62/62 methods avoid null-pointer exceptions

2. Extend type system

3. Evaluate✓

Page 56: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Multimethod control flow

1. Identify idiom

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point n) ;=> -4

Page 57: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

2. Extend type system

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point n) ;=> -4

Multimethod control flow(ann flip [(U Str Int) -> Int])

Str

Int

Page 58: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

2. Extend type system

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point n) ;=> -4

Multimethod control flow(ann flip [(U Str Int) -> Int])

Solution: Assume dispatch when checking methods

Assume: (isa? (class n) String)

Str

Int

Page 59: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

1. Identify idiom

2. Extend type system

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point n) ;=> -4

Multimethod control flow(ann flip [(U Str Int) -> Int])

Solution: Assume dispatch when checking methods

Assume: (isa? (class n) String)

Str

Assume: (isa? (class n) Number)

Int

✓✓

Page 60: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defmulti flip (fn [n] (class n)))

(defmethod flip String [n] (- (Long/parseLong n)))

(defmethod flip Number [n] (- n))

(flip-point n) ;=> -4 1. Identify

idiom2. Extend type

system

Multimethod control flow(ann flip [(U Str Int) -> Int])

3. Evaluate

Evaluation 11 defmulti’s, 89 defmethod’s

Page 61: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Formalism

Page 62: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

Occurrence typing overview

Page 63: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

Under environment where n is of type (U Str Int)

Page 64: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

This expression has type Int

Page 65: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

n:(U Str Int) ⊢ (string? n) : Any ; n : Str | n : (Not Str)

Check condition

Page 66: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

n:(U Str Int) ⊢ (string? n) : Any ; n : Str | n : (Not Str)

n:(U Str Int) ⊢ (- (Long/parseLong n)) : Int

Problem! n should be a Str

Page 67: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

n:(U Str Int) ⊢ (string? n) : Any ; n:Str | n : (Not Str)

n:(U Str Int) ⊢ (- (Long/parseLong n)) : Int

Extend judgment with what we know if expression

is true

Page 68: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

n:(U Str Int) ⊢ (string? n) : Any ; n:Str | n : \(Not Str)

n:(U Str Int), n:Str ⊢ (- (Long/parseLong n)) : Int ✓ ✓

Assume in branch!

Page 69: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (if (string? n) (- (Long/parseLong n)) (- n)) : Int

n:(U Str Int) ⊢ (string? n) : Any ; n:Str | n:(Not Str) Str)

n:(U Str Int), n:Str ⊢ (- (Long/parseLong n)) : Int

Same for ‘else’ branch

n:(U Str Int), n:(Not Str) ⊢ (- n) : Int

✓ ✓

Page 70: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (string? n) : Any ; n:Str | n:(Not Str)

Recap: Judgment extensions

Proposition environment List of currently true propositions

Page 71: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (string? n) : Any ; n:Str | n:(Not Str)

Recap: Judgment extensions

‘Then’ proposition True logical statement when (string? n)

evaluates to a true value

Page 72: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (string? n) : Any ; n:Str | n:(Not Str)

Recap: Judgment extensions

‘Else’ proposition True logical statement when (string? n)

evaluates to a false value

Page 73: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [x : (U Int Str) -> (U nil Class) ; … ; (class x)])

New work: Multimethods + occurrence typing

⊢ (defmethod flip Number [n] (- n)) : …

Page 74: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [n : (U Int Str) -> (U nil Class) ; … ; (class n)])

⊢ (defmethod flip Number [n] (- n)) : …

n:(U Str Int) ⊢ (class n) : (U nil Class) ; … ; (class n)

Symbolic representation of dispatch function’s return value

Page 75: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [n : (U Int Str) -> (U nil Class) ; … ; (class n)])

⊢ (defmethod flip Number [n] (- n)) : …

n:(U Str Int) ⊢ (class n) : (U nil Class) ; … ; (class n)

Remember in multimethod’s type

Page 76: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [n : (U Int Str) -> (U nil Class) ; … ; (class n)])

⊢ (defmethod flip Number [n] (- n)) : …

n:(U Str Int) ⊢ (class n) : (U nil Class) ; … ; (class n)

⊢ Number : (Value Number)

Check dispatch value

Page 77: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [n : (U Int Str) -> (U nil Class) ; … ; (class n)])

⊢ (defmethod flip Number [n] (- n)) : …

n:(U Str Int) ⊢ (class n) : (U nil Class) ; … ; (class n)

n:(U Str Int) ⊢ (- n) : Int ⊢ Number : (Value Number)

Check method body

Page 78: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [n : (U Int Str) -> (U nil Class) ; … ; (class n)])

⊢ (defmethod flip Number [n] (- n)) : …

n:(U Str Int) ⊢ (class n) : (U nil Class) ; … ; (class n)

n:(U Str Int) ⊢ (- n) : Int ⊢ Number : (Value Number)

Assume most general type for parameter

Page 79: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [n : (U Int Str) -> (U nil Class) ; … ; (class n)])

⊢ (defmethod flip Number [n] (- n)) : …

n:(U Str Int) ⊢ (class n) : (U nil Class) ; … ; (class n)

n:(U Str Int) ⊢ (- n) : Int ⊢ Number : (Value Number)

Problem! n should be Int

Page 80: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

n:(U Str Int) ⊢ (defmulti flip (fn [n] (class n))) : (Multi [(U Int Str) -> Int] [n : (U Int Str) -> (U nil Class) ; … ; (class n)])

⊢ (defmethod flip Number [n] (- n)) : …

n:(U Str Int) ⊢ (class n) : (U nil Class) ; … ; (class n)

n:(U Str Int), n:Num ⊢ (- n) : Int ⊢ Number : (Value Number)

Solution Reconstruct

dispatch invariants

n:Num

Page 81: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Evaluation

Page 82: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str)]) (defn upper-case [s] (when s (.toUpperCase s)))

Avoiding null-pointer exceptions

(U nil Str)

StrEvaluation 62/62 methods avoid null-pointer exceptions

Page 83: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(ann upper-case [(U nil Str) -> (U nil Str)]) (defn upper-case [s] (when s (.toUpperCase s)))

Insight: Clojure programmers use simple local reasoning

to avoid null-pointer exceptions

Avoiding null-pointer exceptions

Page 84: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [x y] (merge {:x x} {:y y}))

Absence of map entries

Types must track absence of :x entry to prevent bad type

Page 85: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [x y] (merge {:x x} {:y y}))

Absence of map entries

(HMap :complete? true :mandatory {:y y})

Fully specified HMaps:

Page 86: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

(defn point [x y] (merge {:x x} {:y y}))

Insight: Clojure programmers reason about

the presence and absence of specific map keys

Absence of map entries

Evaluation 27% of keyword lookups on HMaps either optional or absent

Page 87: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Good idioms?

Soundness proof

Are these real idioms?

✓Straightforward design✓

Real idioms?

Popular implementation✓Evaluation✓

Page 88: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Are these real idioms?

Clojure encourages disciplined

programming

Page 89: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Are these real idioms?

Typed Clojure is a real tool used today

Page 90: Practical Optional Types for Clojure - Ambrose BS · 2020. 7. 14. · Ambrose Bonnaire-Sergeant* Rowan Davies** Sam Tobin-Hochstadt* Practical Optional Types for Clojure Typed Clojure

Are these real idioms?

Typed Clojure is a real tool used today

Ambrose Bonnaire-Sergeant

Thanks!

@ambrosebs

https://github.com/clojure/core.typed