Typed Clojure in Practice Ambrose Bonnaire-Sergeant Indiana - - PowerPoint PPT Presentation

typed clojure in practice
SMART_READER_LITE
LIVE PREVIEW

Typed Clojure in Practice Ambrose Bonnaire-Sergeant Indiana - - PowerPoint PPT Presentation

Typed Clojure in Practice Ambrose Bonnaire-Sergeant Indiana University @ambrosebs Lets write some Clojure code ... a hashing function Heres how you call it (summarise [1 2 3]) ;=> <Int> (summarise nil) ;=> <Int>


slide-1
SLIDE 1

Typed Clojure in Practice

Ambrose Bonnaire-Sergeant Indiana University @ambrosebs

slide-2
SLIDE 2

Let’s write some Clojure code

slide-3
SLIDE 3

... a hashing function

slide-4
SLIDE 4

(summarise [1 2 3]) ;=> <Int> (summarise nil) ;=> <Int>

Here’s how you call it

slide-5
SLIDE 5

(summarise []) (summarise ())

is is invalid input

slide-6
SLIDE 6

;; nil or (NonEmptyColl Int) -> Int

e Type is roughly this

slide-7
SLIDE 7

(ns sum) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

Untyped prototype

slide-8
SLIDE 8
slide-9
SLIDE 9
slide-10
SLIDE 10

=> (summarise nil) ;=> 42

slide-11
SLIDE 11

=> (summarise [42 33 32]) => (summarise nil) ;=> 42

slide-12
SLIDE 12

=> (summarise [42 33 32]) java.lang.StackOverflowError at java.lang.Number<init>(Number.java:49) at java.lang.Long<init>(Long.java:684) at java.lang.Long.valueOf(Long.java:577 at stl2014.sum$summarise.invoke(sum.clj:6) at stl2014.sum$summarise.invoke(sum.clj:6) at stl2014.sum$summarise.invoke(sum.clj:6) at stl2014.sum$summarise.invoke(sum.clj:6) at stl2014.sum$summarise.invoke(sum.clj:6) ... => (summarise nil) ;=> 42

slide-13
SLIDE 13

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42))) (ns sum)

Step 1: Require core.typed

slide-14
SLIDE 14

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42))) (ns sum ) (:require [clojure.core.typed :as t])

Step 1: Require core.typed

slide-15
SLIDE 15

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

Step 2: Annotate vars

slide-16
SLIDE 16

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42))) (ann summarise

Step 2: Annotate vars

slide-17
SLIDE 17

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42))) (ann summarise (IFn [(U nil (NonEmptyColl Int)) -> Int]

Step 2: Annotate vars

slide-18
SLIDE 18

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42))) (ann summarise (IFn [(U nil (NonEmptyColl Int)) -> Int] [(U nil (NonEmptyColl Int)) Int -> Int]))

Step 2: Annotate vars

slide-19
SLIDE 19

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42))) (ann summarise (IFn [(U nil (NonEmptyColl Int)) -> Int] [(U nil (NonEmptyColl Int)) Int -> Int])) (IFn [(U nil (NonEmptyColl Int)) -> Int] [(U nil (NonEmptyColl Int)) Int -> Int]))

Aliases for readability

slide-20
SLIDE 20

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) (ann summarise (IFn [(U nil (NonEmptyColl Int)) -> Int] [(U nil (NonEmptyColl Int)) Int -> Int]))

Aliases for readability

slide-21
SLIDE 21

(ann summarise (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) (IFn [(U nil (NonEmptyColl Int)) -> Int] [ (U nil (NonEmptyColl Int)) Int -> Int]))

Aliases for readability

slide-22
SLIDE 22

(ann summarise (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) (defalias NInts “nil or persistent non-empty coll of ints” (IFn [ (U nil (NonEmptyColl Int))

  • > Int]

[ (U nil (NonEmptyColl Int)) Int -> Int])) )

Aliases for readability

slide-23
SLIDE 23

(ann summarise (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) (IFn [

  • > Int]

[ Int -> Int])) (defalias NInts “nil or persistent non-empty coll of ints” (U nil (NonEmptyColl Int)))

Aliases for readability

slide-24
SLIDE 24

(ann summarise (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) (IFn [NInts -> Int] [ NInts Int -> Int])) (defalias NInts “nil or persistent non-empty coll of ints” (U nil (NonEmptyColl Int)))

Aliases for readability

slide-25
SLIDE 25

(ann summarise (IFn [NInts -> Int] [NInts Int -> Int])) (defalias NInts “nil or persistent non-empty coll of ints” (U nil (NonEmptyColl Int))) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq))

Aliases for readability

slide-26
SLIDE 26

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

slide-27
SLIDE 27

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

slide-28
SLIDE 28

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

=> (t/check-ns)

Type mismatch: Expected: NInts Actual: (Seq Int) in: (rest nseq)

slide-29
SLIDE 29

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

slide-30
SLIDE 30

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

(rest [1]) ;=> ()

slide-31
SLIDE 31

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

(rest [1]) ;=> ()

slide-32
SLIDE 32

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

(rest [1]) ;=> ()

slide-33
SLIDE 33

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

(rest [1]) ;=> ()

slide-34
SLIDE 34

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

(rest [1]) ;=> () (rest ()) ;=> ()

slide-35
SLIDE 35

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

(rest [1]) ;=> () (rest ()) ;=> ()

slide-36
SLIDE 36

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (rest nseq) (inc acc)) (first nseq)) 42)))

(rest [1]) ;=> () (rest ()) ;=> () (rest ()) ;=> ()

slide-37
SLIDE 37

(rest [1 2]) ;=> (2) (rest nil) ;=> () (rest []) ;=> ()

slide-38
SLIDE 38

(next [1 2]) ;=> (2) (next nil) ;=> nil (next []) ;=> nil

slide-39
SLIDE 39

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise ( nseq) (inc acc)) (first nseq)) 42))) rest

slide-40
SLIDE 40

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise ( nseq) (inc acc)) (first nseq)) 42)))

slide-41
SLIDE 41

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise ( nseq) (inc acc)) (first nseq)) 42)))

slide-42
SLIDE 42

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise ( nseq) (inc acc)) (first nseq)) 42))) next

slide-43
SLIDE 43

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42)))

slide-44
SLIDE 44

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42)))

=> (t/check-ns)

:ok

slide-45
SLIDE 45
slide-46
SLIDE 46

=> (summarise [42 33 32]) ;=> 1280664 => (summarise nil) ;=> 42

slide-47
SLIDE 47

=> (summarise [42 33 32]) ;=> 1280664 => (summarise nil) ;=> 42 => (summarise [])

slide-48
SLIDE 48

=> (summarise [42 33 32]) ;=> 1280664 => (summarise nil) ;=> 42 => (summarise []) NullPointerException clojure.lang.Numbers.ops (Numbers.java:961) clojure.lang.Numbers.multiply (Numbers.java:146) stl2014.sum/summarise (form-init6981015802397519697.clj:16) stl2014.sum/summarise (form-init6981015802397519697.clj:13) stl2014.sum/eval3757 (form-init6981015802397519697.clj:1) clojure.lang.Compiler.eval (Compiler.java:6703) ...

slide-49
SLIDE 49

(ann summarise (IFn [NInts -> Int] [NInts Int -> Int])) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42))) Int))) Coll NonEmpty (defalias NInts (U nil ( non-empty "nil or a persistent collection of integers"

Update the annotation

slide-50
SLIDE 50

(ann summarise (IFn [NInts -> Int] [NInts Int -> Int])) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42))) Int))) Coll (defalias NInts (U nil ( "nil or a persistent collection of integers"

Update the annotation

slide-51
SLIDE 51

(defalias NInts "nil or a persistent collection of integers" (U nil (Coll Int))) (ann summarise (IFn [NInts -> Int] [NInts Int -> Int])) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42)))

Update the annotation

slide-52
SLIDE 52

(ann summarise (IFn [NInts -> Int] [NInts Int -> Int])) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42)))

slide-53
SLIDE 53

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42)))

Check with new type

slide-54
SLIDE 54

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42))) => (t/check-ns)

Type mismatch: Expected: Number Actual: (U nil Int) in: (first nseq)

Check with new type

slide-55
SLIDE 55

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42))) => (t/check-ns)

Type mismatch: Expected: Number Actual: (U nil Int) in: (first nseq)

Need a better test

slide-56
SLIDE 56

(defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if nseq (* (summarise (next nseq) (inc acc)) (first nseq)) 42))) => (t/check-ns)

Type mismatch: Expected: Number Actual: (U nil Int) in: (first nseq)

[ ] [ ] [ ] [ ]

Need a better test

slide-57
SLIDE 57

(seq [1 2]) ;=> (1 2) (seq []) ;=> nil (seq nil) ;=> nil

slide-58
SLIDE 58

(ann summarise (IFn [NInts -> Int] [NInts Int -> Int])) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if (* (summarise (next nseq) (inc acc)) (first nseq)) 42))) nseq

Update implementation

slide-59
SLIDE 59

(ann summarise (IFn [NInts -> Int] [NInts Int -> Int])) (defn summarise ([nseq] (summarise nseq 0)) ([nseq acc] (if (* (summarise (next nseq) (inc acc)) (first nseq)) 42))) (seq nseq) => (t/check-ns)

:ok

Update implementation

slide-60
SLIDE 60
slide-61
SLIDE 61

=> (summarise [42 33 32]) ;=> 1280664 => (summarise nil) ;=> 42 => (summarise []) ;=> 42

slide-62
SLIDE 62
slide-63
SLIDE 63

Typed Clojure

is an

Optional Type System

that

catches type errors

in

real Clojure code

slide-64
SLIDE 64
slide-65
SLIDE 65
slide-66
SLIDE 66

Where have we come from?

slide-67
SLIDE 67

2011

slide-68
SLIDE 68
slide-69
SLIDE 69
slide-70
SLIDE 70
slide-71
SLIDE 71
slide-72
SLIDE 72

2012

slide-73
SLIDE 73

2012

slide-74
SLIDE 74
slide-75
SLIDE 75
slide-76
SLIDE 76
slide-77
SLIDE 77
slide-78
SLIDE 78
slide-79
SLIDE 79
slide-80
SLIDE 80

Sam Tobin-Hochstadt Ambrose Bonnaire-Sergeant 2014

slide-81
SLIDE 81

Where are we now?

slide-82
SLIDE 82
slide-83
SLIDE 83

Colin Fleming

slide-84
SLIDE 84
slide-85
SLIDE 85

}

Function application error

slide-86
SLIDE 86
slide-87
SLIDE 87

Francesco Bellomi

CrossClj.info

slide-88
SLIDE 88

CrossClj.info

slide-89
SLIDE 89

TypedClojure.vim

slide-90
SLIDE 90

Minori Yamashita Di Xu

slide-91
SLIDE 91

Typed Clojure 101

slide-92
SLIDE 92

Occurrence Typing

slide-93
SLIDE 93

(ns gen-vec) (defn gen-vec [n-or-v] (if (number? n-or-v) (vec (range n-or-v)) n-or-v))

slide-94
SLIDE 94

(ns gen-vec) (defn gen-vec [n-or-v] (if (number? n-or-v) (vec (range n-or-v)) n-or-v)) (gen-vec 5) ;=> [0 1 2 3 4] (gen-vec [1 2 3 4]) ;=> [1 2 3 4]

slide-95
SLIDE 95

(gen-vec )

slide-96
SLIDE 96

(gen-vec ) (if (number? ) (vec (range )) ))

5 5 5 5

(vec (range ))

5

[0 1 2 3 4] true

slide-97
SLIDE 97

(gen-vec )

slide-98
SLIDE 98

(gen-vec ) (if (number? ) (vec (range )) ))

[1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]

false

slide-99
SLIDE 99

(ns gen-vec (:require [clojure.core.typed :refer [U Num Vec Int ann] :as t])) (ann gen-vec [(U Num (Vec Int)) -> (Vec Int)]) (defn gen-vec [n-or-v] (if (number? n-or-v) (vec (range n-or-v)) n-or-v))

slide-100
SLIDE 100

(ns gen-vec (:require [clojure.core.typed :refer [U Num Vec Int ann] :as t])) (ann gen-vec [(U Num (Vec Int)) -> (Vec Int)]) (defn gen-vec [n-or-v] (if (number? n-or-v) (vec (range n-or-v)) n-or-v))

=> (t/check-ns)

:ok

(ann clojure.core/number? (Pred Num))

slide-101
SLIDE 101

HMaps

slide-102
SLIDE 102

(assoc {} :a 1 :b “foo” :c ‘baz)

(HMap :mandatory {:a Num :b Str :c Sym}) is of type

slide-103
SLIDE 103

(assoc {} :a 1 :b “foo” :c ‘baz)

(HMap :mandatory {:a Num :b Str :c Sym}) is of type ‘{:a Num :b Str :c Sym} aka.

slide-104
SLIDE 104

Multimethods

slide-105
SLIDE 105

(ann f [(U Num Sym) -> Num]) (defmulti f class) (defmethod f Number [n] (inc n)) (defmethod f Symbol [s] (count (name s)))

slide-106
SLIDE 106

Variable-Arity Polymorphism

slide-107
SLIDE 107

;; 2 args (map (fn [a :- Int] (inc a)) [1 2 3]) ;; 3 args (map (fn [a :- Int b :- Sym] [(inc a) (name b)]) [1 2 3] ['a 'b 'c])

Calling map

slide-108
SLIDE 108

;; 2 args (All [x y] [[x -> y] (U nil (Seqable x))

  • > (Seq y)])

;; 3 args (All [x y z] [[x z -> y] (U nil (Seqable x)) (U nil (Seqable z))

  • > (Seq y)])

map types

slide-109
SLIDE 109

All map types

;; n args (All [x y z ...] [[x z ... z -> y] (U nil (Seqable x)) (U nil (Seqable z)) ... z

  • > (Seq y)])
slide-110
SLIDE 110

Datatypes and Protocols

slide-111
SLIDE 111

(t/defprotocol Adder (add [this y :- Num] :- Adder)) (t/deftype A [x :- Num] Adder (add [this y] (+ x y))) (add (A. 1) 34)

slide-112
SLIDE 112

Java Interop

slide-113
SLIDE 113

(ann grand-parent [File -> (U nil File)]) (defn grand-parent [^File f] (let [p1 (.getParentFile f)] (.getParentFile p1)))

slide-114
SLIDE 114

Cannot call instance method java.io.File/getParentFile

  • n type

(U nil File)

=> (t/check-ns)

(ann grand-parent [File -> (U nil File)]) (defn grand-parent [^File f] (let [p1 (.getParentFile f)] (.getParentFile p1)))

slide-115
SLIDE 115

(ann grand-parent [File -> (U nil File)]) (defn grand-parent [^File f] (let [p1 (.getParentFile f)] (.getParentFile p1)))

slide-116
SLIDE 116

(ann grand-parent [File -> (U nil File)]) (defn grand-parent [^File f] (let [p1 (.getParentFile f)]

slide-117
SLIDE 117

(ann grand-parent [File -> (U nil File)]) (defn grand-parent [^File f] (let [p1 (.getParentFile f)]

=> (t/check-ns)

(.getParentFile p1)))) (when p1

:ok

slide-118
SLIDE 118

Sample Application

https://github.com/typedclojure/core.typed-example

slide-119
SLIDE 119

lein Config

(defproject fire.simulate "0.1.0-SNAPSHOT" ... :profiles {:dev {:dependencies [[org.clojure/core.typed "0.2.72"]]}} :dependencies [[org.clojure/core.typed.rt "0.2.72"] ...] :plugins [[lein-typed "0.3.5"]] :core.typed {:check [fire.simulate fire.main fire.gnuplot fire.simulate.percolation fire.simulate-test]})

slide-120
SLIDE 120

lein-typed

$ lein typed check ... Start checking fire.simulate ... Start checking fire.simulate.percolate ... :ok $ https://github.com/typedclojure/lein-typed

slide-121
SLIDE 121

Refer clojure.core

(ns fire.simulate "This namespace defines operations for the study of percolation in the forest fire simulation." (:refer-clojure :exclude [for fn doseq dotimes]) (:require [clojure.core.typed :refer [for fn doseq dotimes] :as t]) (:import (java.io Writer)))

slide-122
SLIDE 122

Aliases

(defalias Point "A point in 2d space." '[Int Int])

slide-123
SLIDE 123

Aliases

(defalias Grid "An immutable snapshot of the world state.

  • :grid the grid
  • :rows number of rows
  • :cols number of columns
  • :history a chronological vector of interesting data of

previous states. See GridHistory.

  • :q the initial probability of a green tree at time 0 at a site
  • :p the probability a tree will grow in an empty site
  • :f the probability a site with a tree will burn (lightning)
  • :frame the frame number corresponding to the grid,

displayed in the gnuplot graph" '{:grid GridVector :rows Int :cols Int :history GridHistory :q Num :p Num :f Num :frame Int})

slide-124
SLIDE 124

Aliases

;----------------------------------------------------------------- ; Type Aliases ;----------------------------------------------------------------- (defalias State "A point can either be empty, a tree, or a burning tree." (U ':burning ':tree ':empty)) (defalias GridHistoryEntry "Some data about a particular state.
  • :nburning number of burning points at this state
  • :ntrees number of green trees at this state"
'{:nburning Int :ntrees Int :nempty Int}) (defalias GridHistory "A vector of history states on a grid" (Vec GridHistoryEntry)) (defalias GridVector "A vector of vectors of states representing a cellular automata lattice." (Vec (t/Vec State))) (defalias Grid "An immutable snapshot of the world state.
  • :grid the grid
  • :rows number of rows
  • :cols number of columns
  • :history a chronological vector of interesting data of previous states.
See GridHistory.
  • :q the initial probability of a green tree at time 0 at a site
  • :p the probability a tree will grow in an empty site
  • :f the probability a site with a tree will burn (lightning)
  • :frame the frame number corresponding to the grid, displayed in the gnuplot graph"
'{:grid GridVector :rows Int :cols Int :history GridHistory :q Num :p Num :f Num :frame Int}) (defalias Point "A point in 2d space." '[Int Int])
slide-125
SLIDE 125

Succinct Annotations

(ann state->number [State -> Long]) (defn state->number "Convert the keyword representation of a state to a number usable by Gnuplot for plotting color gradient." [k] (case k :empty 0 :tree 1 :burning 2))

slide-126
SLIDE 126

(defalias GridVector (Vec (Vec State))) (defalias Grid '{:grid GridVector ...}) ... (ann flat-grid [Grid -> (Coll State)]) (defn flat-grid [{:keys [grid]}] (ann-form grid GridVector) (apply concat grid))

Static Assertions

slide-127
SLIDE 127

(defalias GnuplotP "A gnuplot process.

  • :proc The Process instance
  • :out A Writer piping to gnuplot's stdin
  • :in A Reader reading from gnuplot's stdout"

'{:proc Process, :out Writer, :in Reader}) (ann stop [GnuplotP -> Any]) (defn stop "Stop gnuplot process." [{:keys [^Process proc]}] (.destroy proc))

Resolve Reflection

slide-128
SLIDE 128

; We know these method never return null. (non-nil-return java.lang.Process/getOutputStream :all) (non-nil-return java.lang.Process/getInputStream :all) (non-nil-return java.lang.ProcessBuilder/start :all) (ann start [-> GnuplotP]) (defn start "Start gnuplot process." [] (let [proc (-> (doto (ProcessBuilder. '("gnuplot" "-persist")) (.redirectErrorStream true)) .start)

  • ut (io/writer (.getOutputStream proc))

in (io/reader (.getInputStream proc))] {:proc proc :out out :in in}))

Java Overrides

slide-129
SLIDE 129

Case Study

slide-130
SLIDE 130
slide-131
SLIDE 131
slide-132
SLIDE 132
slide-133
SLIDE 133

80% 20%

Typed Clojure Clojure

Approx. Lines of Code

(Total ~50,000)

slide-134
SLIDE 134

λ

22% 32% 46%

Checked Not checked Libraries

Var Annotations

(Total 588)

slide-135
SLIDE 135

Sample code

slide-136
SLIDE 136

(defn encrypt-keypair [{:keys [private-key] :as keypair}] (assoc (dissoc keypair :private-key) :encrypted-private-key (encrypt private-key)))

slide-137
SLIDE 137

(defn encrypt-keypair [{:keys [private-key] :as keypair}] (assoc (dissoc keypair :private-key) :encrypted-private-key (encrypt private-key))) (ann encrypt-keypair [RawKeyPair -> EncryptedKeyPair])

slide-138
SLIDE 138
slide-139
SLIDE 139
slide-140
SLIDE 140

(defalias RawKeyPair "A keypair with a raw private key" (HMap :mandatory {:public-key RawKey, :private-key RawKey} :complete? true)) (defalias EncryptedKeyPair "A keypair with an encrypted private key" (HMap :mandatory {:public-key RawKey, :encrypted-private-key EncryptedKey} :complete? true))

slide-141
SLIDE 141
slide-142
SLIDE 142
slide-143
SLIDE 143

(ann encrypt-keypair [RawKeyPair -> EncryptedKeyPair]) (dissoc keypair :private-key) (defn encrypt-keypair [{:keys [private-key] :as keypair}] (assoc :encrypted-private-key (encrypt private-key)))

slide-144
SLIDE 144

(ann encrypt-keypair [RawKeyPair -> EncryptedKeyPair])

Type mismatch: Expected: EncryptedKeyPair Actual: (HMap :mandatory {:encrypted-private-key EncryptedKey, :public-key RawKey, :private-key RawKey} :complete? true)

=> (t/check-ns) keypair (defn encrypt-keypair [{:keys [private-key] :as keypair}] (assoc :encrypted-private-key (encrypt private-key)))

slide-145
SLIDE 145

{:a 1} 41% 59%

HMap Non-HMap

Type aliases

(Total 64)

slide-146
SLIDE 146

e Future

slide-147
SLIDE 147

Typed ClojureScript

slide-148
SLIDE 148

Towards Gradual Typing

slide-149
SLIDE 149

Towards Gradual Typing

slide-150
SLIDE 150

Towards Gradual Typing

slide-151
SLIDE 151

Towards Gradual Typing

slide-152
SLIDE 152

Towards Gradual Typing

slide-153
SLIDE 153
slide-154
SLIDE 154
slide-155
SLIDE 155

Call to Action

Annotate your libraries

slide-156
SLIDE 156

Conclusion

  • Typed Clojure works in Production
  • Get it at typedclojure.org
  • Annotate your libraries
slide-157
SLIDE 157

Conclusion

  • Typed Clojure works in Production
  • Get it at typedclojure.org
  • Annotate your libraries

ank you

Ambrose Bonnaire-Sergeant @ambrosebs