157
Typed Clojure in Practice Ambrose Bonnaire-Sergeant Indiana University @ambrosebs

Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Typed Clojure in Practice

Ambrose Bonnaire-SergeantIndiana University

@ambrosebs

Page 2: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Let’s write someClojure code

Page 3: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

... a hashing function

Page 4: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Here’s how you call it

Page 5: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(summarise [])(summarise ())

is is invalid input

Page 6: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

;; nil or (NonEmptyColl Int) -> Int

e Type is roughly this

Page 7: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ns sum)

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

Untyped prototype

Page 8: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 9: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 10: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

=> (summarise nil);=> 42

Page 11: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

=> (summarise [42 33 32])

=> (summarise nil);=> 42

Page 12: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

=> (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

Page 13: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 14: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 15: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Step 2: Annotate vars

Page 16: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(ann summarise

Step 2: Annotate vars

Page 17: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 18: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 19: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 20: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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]))

Aliases for readability

Page 21: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ann summarise

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

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

Aliases for readability

Page 22: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ann summarise

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

(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

Page 23: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ann summarise

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

(IFn [ -> Int] [ Int -> Int]))

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

Aliases for readability

Page 24: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ann summarise

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

(IFn [NInts -> Int] [ NInts Int -> Int]))

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

Aliases for readability

Page 25: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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)) 42)))

Aliases for readability

Page 26: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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)) 42)))

Page 27: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 28: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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)

Page 29: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 30: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest [1]);=> ()

Page 31: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest [1]);=> ()

Page 32: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest [1]);=> ()

Page 33: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest [1]);=> ()

Page 34: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest [1]);=> ()

(rest ());=> ()

Page 35: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest [1]);=> ()

(rest ());=> ()

Page 36: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest [1]);=> ()

(rest ());=> ()(rest ());=> ()

Page 37: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(rest nil);=> ()

(rest []);=> ()

Page 38: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(next nil);=> nil

(next []);=> nil

Page 39: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

rest

Page 40: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 41: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 42: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

next

Page 43: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 44: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

:ok

Page 45: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 46: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

=> (summarise nil);=> 42

Page 47: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

=> (summarise nil);=> 42

=> (summarise [])

Page 48: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

=> (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) ...

Page 49: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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)))CollNonEmpty

(defalias NInts

(U nil (non-empty "nil or a persistent collection of integers"

Update the annotation

Page 50: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 51: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 52: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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)))

Page 53: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ns stl2014.sum (:require [clojure.core.typed :as t :refer [ann IFn U Coll Int defalias]]))

(defalias NInts "nil or a non-empty persistent collection of integers" (U nil (Coll Int)))

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

Check with new type

Page 54: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ns stl2014.sum (:require [clojure.core.typed :as t :refer [ann IFn U Coll Int defalias]]))

(defalias NInts "nil or a non-empty persistent collection of integers" (U nil (Coll Int)))

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

=> (t/check-ns)

Type mismatch:

Expected: NumberActual: (U nil Int)

in: (first nseq)

Check with new type

Page 55: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ns stl2014.sum (:require [clojure.core.typed :as t :refer [ann IFn U Coll Int defalias]]))

(defalias NInts "nil or a non-empty persistent collection of integers" (U nil (Coll Int)))

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

=> (t/check-ns)

Type mismatch:

Expected: NumberActual: (U nil Int)

in: (first nseq)

Need a better test

Page 56: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ns stl2014.sum (:require [clojure.core.typed :as t :refer [ann IFn U Coll Int defalias]]))

(defalias NInts "nil or a non-empty persistent collection of integers" (U nil (Coll Int)))

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

=> (t/check-ns)

Type mismatch:

Expected: NumberActual: (U nil Int)

in: (first nseq)

[ ]

[ ][ ]

[ ]

Need a better test

Page 57: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

(seq []);=> nil

(seq nil);=> nil

Page 58: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 59: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 60: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 61: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

=> (summarise nil);=> 42

=> (summarise []);=> 42

Page 62: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 63: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Typed Clojure is an

Optional Type Systemthat

catches type errors in

real Clojure code

Page 64: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 65: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 66: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Where have we come from?

Page 67: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

2011

Page 68: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 69: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 70: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 71: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 72: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

2012

Page 73: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

2012

Page 74: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 75: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 76: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 77: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 78: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 79: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 80: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Sam Tobin-Hochstadt

Ambrose Bonnaire-Sergeant

2014

Page 81: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Where are we now?

Page 82: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 83: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Colin Fleming

Page 84: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 85: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

} Functionapplicationerror

Page 86: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 87: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Francesco Bellomi

CrossClj.info

Page 88: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

CrossClj.info

Page 89: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

TypedClojure.vim

Page 90: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Minori Yamashita Di Xu

Page 91: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Typed Clojure101

Page 92: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Occurrence Typing

Page 93: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ns gen-vec)

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

Page 94: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 95: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(gen-vec )

Page 96: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(gen-vec )

(if (number? ) (vec (range )) ))

5

55

5

(vec (range ))5

[0 1 2 3 4]

true

Page 97: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(gen-vec )

Page 98: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(gen-vec )

(if (number? ) (vec (range )) ))

[1 2 3]

[1 2 3][1 2 3]

[1 2 3]

[1 2 3]

false

Page 99: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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))

Page 100: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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))

Page 101: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

HMaps

Page 102: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

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

is of type

Page 103: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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.

Page 104: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Multimethods

Page 105: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(ann f [(U Num Sym) -> Num])(defmulti f class)

(defmethod f Number [n] (inc n))(defmethod f Symbol [s] (count (name s)))

Page 106: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Variable-ArityPolymorphism

Page 107: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

;; 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

Page 108: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

;; 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

Page 109: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

All map types

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

Page 110: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Datatypesand

Protocols

Page 111: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 112: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Java Interop

Page 113: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 114: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Cannot call instance method java.io.File/getParentFile

on 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)))

Page 115: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 116: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 117: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

=> (t/check-ns)

(.getParentFile p1))))(when p1

:ok

Page 118: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Sample Applicationhttps://github.com/typedclojure/core.typed-example

Page 119: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

lein Con"g(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]})

Page 120: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

lein-typed

$ lein typed check...Start checking fire.simulate...Start checking fire.simulate.percolate...:ok$

https://github.com/typedclojure/lein-typed

Page 121: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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)))

Page 122: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Aliases

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

Page 123: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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})

Page 124: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 125: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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))

Page 126: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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

Page 127: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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 Re#ection

Page 128: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

; 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) out (io/writer (.getOutputStream proc)) in (io/reader (.getInputStream proc))] {:proc proc :out out :in in}))

Java Overrides

Page 129: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Case Study

Page 130: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 131: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 132: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 133: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

80%

20%

Typed ClojureClojure

Approx. Lines of Code(Total ~50,000)

Page 134: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

λ22%

32%

46%CheckedNot checkedLibraries

Var Annotations(Total 588)

Page 135: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Sample code

Page 136: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

Page 137: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

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

Page 138: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 139: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 140: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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))

Page 141: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 142: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 143: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

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

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

Page 144: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

(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)))

Page 145: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

{:a 1}

41%59%

HMapNon-HMap

Type aliases (Total 64)

Page 146: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

e Future

Page 147: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Typed ClojureScript

Page 148: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Towards Gradual Typing

Page 149: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Towards Gradual Typing

Page 150: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Towards Gradual Typing

Page 151: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Towards Gradual Typing

Page 152: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Towards Gradual Typing

Page 153: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 154: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty
Page 155: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Call to ActionAnnotate

your

libraries

Page 156: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Conclusion• Typed Clojure works in Production

• Get it at typedclojure.org

• Annotate your libraries

Page 157: Typed Clojure in Practice - Ambrose BS · 2020. 7. 14. · (ns stl2014.sum (:require [clojure.core.typed :as t:refer [ann IFn U Coll Int defalias]])) (defalias NInts "nil or a non-empty

Conclusion• Typed Clojure works in Production

• Get it at typedclojure.org

• Annotate your libraries

ank youAmbrose Bonnaire-Sergeant

@ambrosebs