Misusing the Type System for & Ian Dees @undees PNSQC 2015 - - PowerPoint PPT Presentation

misusing the type system for
SMART_READER_LITE
LIVE PREVIEW

Misusing the Type System for & Ian Dees @undees PNSQC 2015 - - PowerPoint PPT Presentation

Misusing the Type System for & Ian Dees @undees PNSQC 2015 Brewing for Maintainability Our contaminants: s k a e l y r o m Failing e M regression tests C r a s h e s Poor repeatability Data corruption s e h


slide-1
SLIDE 1

Misusing the Type System for &

Ian Dees • @undees PNSQC 2015

slide-2
SLIDE 2

Brewing for Maintainability

slide-3
SLIDE 3

Our contaminants:

slide-4
SLIDE 4

M e m

  • r

y l e a k s Data corruption Failing regression tests C r a s h e s Poor repeatability S e c u r i t y b r e a c h e s

slide-5
SLIDE 5

Traditional remedies:

slide-6
SLIDE 6

Code review Unit tests Exploratory testing “Just be careful”

slide-7
SLIDE 7

Another proposal:

slide-8
SLIDE 8

Lean on the type system

slide-9
SLIDE 9

Agenda:

slide-10
SLIDE 10
  • 1. Ways you may already be using types
  • 2. Denotational design
  • 3. Applying the ideas in the real world
slide-11
SLIDE 11
  • 1. Ways you may already


be using types:

slide-12
SLIDE 12

Memory leaks

slide-13
SLIDE 13

Sandwich* makeSandwich(); Sandwich* sandwich = makeSandwich(); // who cleans up?

slide-14
SLIDE 14

shared_ptr<Sandwich> makeSandwich(); auto sandwich = makeSandwich(); // cleanup is automatic!

slide-15
SLIDE 15

$ find . -name \*.cpp | xargs grep new

slide-16
SLIDE 16

Crashes

slide-17
SLIDE 17

int findSandwich(string name); int found = findSandwich("cheese"); cout << Sandwiches[found] << endl;

slide-18
SLIDE 18

// returns -1 if not found int findSandwich(string name); int found = findSandwich("cheese"); cout << Sandwiches[found] << endl; 


slide-19
SLIDE 19
  • ptional<size_t> findSandwich(string name);

auto found = findSandwich("cheese"); cout << Sandwiches[found] << endl; // WON'T COMPILE! ^^^^^

slide-20
SLIDE 20
  • ptional<size_t> findSandwich(string name);

auto found = findSandwich("cheese"); if (found) { cout << Sandwiches[*found] << endl; }

slide-21
SLIDE 21

Making errors
 inexpressible

slide-22
SLIDE 22
  • 2. Denotational Design
slide-23
SLIDE 23

–Conal Elliott,
 “Denotational design with type class morphisms”

“When designing software, in addition to innovating in your implementations, relate them to precise and familiar semantic models.”

slide-24
SLIDE 24

–Conal Elliott,
 “Denotational design with type class morphisms”

“When designing software, in addition to innovating in your implementations, relate them to precise and familiar semantic models.”

slide-25
SLIDE 25

In other words…

  • 1. Sketch your design in clear notation
  • 2. Implement it in your “day job” language
slide-26
SLIDE 26

In other words…

  • 1. Sketch your design in (some kind of) clear notation
  • 2. Implement it in your “day job” language
slide-27
SLIDE 27

What kind of clear notation?

slide-28
SLIDE 28

Math

(or a sufficiently math-y programming language)

slide-29
SLIDE 29

Meet Idris

slide-30
SLIDE 30

Haskell’s cousin

slide-31
SLIDE 31
  • - A cousin of Haskell
slide-32
SLIDE 32
  • - A cousin of Haskell

twice x = 2 * x

slide-33
SLIDE 33
  • - A cousin of Haskell

twice : Int -> Int twice x = 2 * x

slide-34
SLIDE 34
  • - A cousin of Haskell

twice : Int -> Int twice x = 2 * x twice 42 —-> 84 : Int

slide-35
SLIDE 35

Dependently typed

slide-36
SLIDE 36

Types can be inputs
 to functions

slide-37
SLIDE 37
  • - Types can be inputs to functions

isNumeric : Type -> Bool isNumeric Int = True isNumeric String = False

slide-38
SLIDE 38
  • - Types can be inputs to functions

isNumeric : Type -> Bool isNumeric Int = True isNumeric String = False isNumeric Int

  • -> True : Bool
slide-39
SLIDE 39

Types can depend


  • n values
slide-40
SLIDE 40
  • - Idris, before we add dependent types

data Vec : Type -> Type where Nil : Vec a (::) : a -> Vec a -> Vec a
 


slide-41
SLIDE 41
  • - Idris, before we add dependent types

data Vec : Type -> Type where Nil : Vec a (::) : a -> Vec a -> Vec a
 
 False :: True :: (the (Vec Bool) Nil)

  • -> [False,True] : Vec Bool
slide-42
SLIDE 42

// C++ template <typename T> class vec { public: static vec<T> nil; vec<T> insert(T); };

slide-43
SLIDE 43
  • - Idris

data Vec : Nat -> Type -> Type where Nil : Vec Z a (::) : a -> Vec k a -> Vec (S k) a

slide-44
SLIDE 44

// C++ template <size_t k, typename T> class vec { public: static vec<0, T> nil; vec<k + 1, T> insert(T); };

slide-45
SLIDE 45

(an aside on numbers)

slide-46
SLIDE 46
  • - Peano numbers

Z --> 0 S Z --> 1 ("successor to zero") S (S Z) --> 2 ("successor to successor
 to zero")

slide-47
SLIDE 47

42

slide-48
SLIDE 48

…is syntactic sugar for …

slide-49
SLIDE 49

S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S Z))))))))))))))))
 ))))))))))))))))))))))) ))

slide-50
SLIDE 50

add : Vec n a -> Vec m a -> Vec (n + m) a

slide-51
SLIDE 51

add : Vec n a -> Vec m a -> Vec (n + m) a add Nil ys = ys add (x :: xs) ys = x :: (add xs ys)

slide-52
SLIDE 52

Y

  • u can do math

  • n meaning
slide-53
SLIDE 53

Proofs

slide-54
SLIDE 54

add : Nat -> Nat -> Nat add Z y = y add (S x) y = S (add x y)

slide-55
SLIDE 55

How would we check that addition is commutative? (x + y = y + x)

slide-56
SLIDE 56

Tests?

slide-57
SLIDE 57

3 + 2

  • -> 5 : Integer

2 + 3

  • -> 5 : Integer
slide-58
SLIDE 58

–Philip Wadler,
 “Propositions as Types”

“For each proof of a given proposition, there is a program of the corresponding type—and vice versa.”

slide-59
SLIDE 59

Proofs are programs

slide-60
SLIDE 60
  • - Given natural numbers x and y,
  • - x + y equals y + x
slide-61
SLIDE 61
  • - Given natural numbers x and y,
  • - x + y equals y + x

addCommutes : (x : Nat) -> (y : Nat) -> plus x y = plus y x

slide-62
SLIDE 62

Proof by induction

Base case
 “It’s true for zero” Inductive step
 “If it’s true for x, then it’s true for x + 1”

slide-63
SLIDE 63

addCommutes Z y = ?base addCommutes (S x) y = let hypothesis = addCommutes x y in ?inductive

slide-64
SLIDE 64

$ idris math.idr > :m Global metavariables: [Math.inductive,Math.base]

slide-65
SLIDE 65

> :p base

  • --------- Goal: ----------

{ hole 0 }: (y : Nat) -> y = plus y Z

slide-66
SLIDE 66

> :p base

  • --------- Goal: ----------

{ hole 0 }: (y : Nat) -> y = plus y Z

slide-67
SLIDE 67

> intros

  • --------- Other goals: ----------

{ hole 0 }

  • --------- Assumptions: ----------

y : Nat

  • --------- Goal: ----------

{ hole 1 }: y = plus y Z

slide-68
SLIDE 68

> intros

  • --------- Other goals: ----------

{ hole 0 }

  • --------- Assumptions: ----------

y : Nat

  • --------- Goal: ----------

{ hole 1 }: y = plus y Z

slide-69
SLIDE 69
  • - https://github.com/idris-lang/Idris-dev/blob/master/libs/

  • - prelude/Prelude/Nat.idr

total plusZeroRightNeutral : (left : Nat) -> left + 0 = left

slide-70
SLIDE 70

> rewrite (plusZeroRightNeutral y)

  • --------- Other goals: ----------

{ hole 1 } { hole 0 }

  • --------- Assumptions: ----------

y : Nat

  • --------- Goal: ----------

{ hole 2 }: plus y Z = plus y Z

slide-71
SLIDE 71

> rewrite (plusZeroRightNeutral y)

  • --------- Other goals: ----------

{ hole 1 } { hole 0 }

  • --------- Assumptions: ----------

y : Nat

  • --------- Goal: ----------

{ hole 2 }: plus y Z = plus y Z

slide-72
SLIDE 72

> trivial base: No more goals. > qed Proof completed! Math.base = proof intros rewrite (plusZeroRightNeutral y) trivial

slide-73
SLIDE 73

And now , for the inductive step

slide-74
SLIDE 74

> :m Global metavariables: [Math.inductive]

slide-75
SLIDE 75

> :p inductive

  • --------- Goal: ----------

{ hole 0 }: (x : Nat) -> (y : Nat) -> (plus x y = plus y x) -> S (plus x y) = plus y (S x)

slide-76
SLIDE 76

> :p inductive

  • --------- Goal: ----------

{ hole 0 }: (x : Nat) -> (y : Nat) -> (plus x y = plus y x) -> S (plus x y) = plus y (S x)

slide-77
SLIDE 77

> intros

  • --------- Other goals: ----------

{ hole 2 } { hole 1 } { hole 0 }

  • --------- Assumptions: ----------

x : Nat y : Nat hypothesis : plus x y = plus y x

  • --------- Goal: ----------

{ hole 3 }: S (plus x y) = plus y (S x)

slide-78
SLIDE 78

> intros

  • --------- Other goals: ----------

{ hole 2 } { hole 1 } { hole 0 }

  • --------- Assumptions: ----------

x : Nat y : Nat hypothesis : plus x y = plus y x

  • --------- Goal: ----------

{ hole 3 }: S (plus x y) = plus y (S x)

slide-79
SLIDE 79
  • - https://github.com/idris-lang/Idris-dev/blob/master/libs/

  • - prelude/Prelude/Nat.idr

total plusSuccRightSucc :
 (left : Nat) ->
 (right : Nat) ->
 S (left + right) = left + (S right)

slide-80
SLIDE 80

> rewrite (plusSuccRightSucc y x)

  • --------- Other goals: ----------

{ hole 3 } { hole 2 } { hole 1 } { hole 0 }

  • --------- Assumptions: ----------

x : Nat y : Nat hypothesis : plus x y = plus y x

  • --------- Goal: ----------

{ hole 4 }: S (plus x y) = S (plus y x)

slide-81
SLIDE 81

> rewrite (plusSuccRightSucc y x)

  • --------- Other goals: ----------

{ hole 3 } { hole 2 } { hole 1 } { hole 0 }

  • --------- Assumptions: ----------

x : Nat y : Nat hypothesis : plus x y = plus y x

  • --------- Goal: ----------

{ hole 4 }: S (plus x y) = S (plus y x)

slide-82
SLIDE 82

> rewrite (plusSuccRightSucc y x)

  • --------- Other goals: ----------

{ hole 3 } { hole 2 } { hole 1 } { hole 0 }

  • --------- Assumptions: ----------

x : Nat y : Nat hypothesis : plus x y = plus y x

  • --------- Goal: ----------

{ hole 4 }: S (plus x y) = S (plus y x)

slide-83
SLIDE 83

> rewrite hypothesis

  • --------- Other goals: ----------

{ hole 4 } { hole 3 } { hole 2 } { hole 1 } { hole 0 }

  • --------- Assumptions: ----------

x : Nat y : Nat hypothesis : plus x y = plus y x

  • --------- Goal: ----------

{ hole 5 }: S (plus x y) = S (plus x y)

slide-84
SLIDE 84

> rewrite hypothesis

  • --------- Other goals: ----------

{ hole 4 } { hole 3 } { hole 2 } { hole 1 } { hole 0 }

  • --------- Assumptions: ----------

x : Nat y : Nat hypothesis : plus x y = plus y x

  • --------- Goal: ----------

{ hole 5 }: S (plus x y) = S (plus x y)

slide-85
SLIDE 85

> trivial inductive: No more goals. > qed Proof completed! Math.inductive = proof intros rewrite (plusSuccRightSucc y x) rewrite hypothesis trivial

slide-86
SLIDE 86
slide-87
SLIDE 87
  • 3. Applying the ideas


in the real world

slide-88
SLIDE 88

Mission:
 Edit audio clips

slide-89
SLIDE 89
slide-90
SLIDE 90

class Audio { public: };

slide-91
SLIDE 91

class Audio { public: size_t count() const; };

slide-92
SLIDE 92

class Audio { public: size_t count() const; int16_t operator[](size_t i) const; int16_t& operator[](size_t i); };

slide-93
SLIDE 93

class Audio { public: size_t count() const; int16_t operator[](size_t i) const; int16_t& operator[](size_t i); double timeAtZero() const; double sampleRate() const; };

slide-94
SLIDE 94

class Audio { public: size_t count() const; int16_t operator[](size_t i) const; int16_t& operator[](size_t i); double timeAtZero() const; double sampleRate() const; double verticalScale() const; };

slide-95
SLIDE 95

Audio audio; double time = 0.5; size_t index = (time - audio.timeAtZero()) / audio.sampleRate();

slide-96
SLIDE 96

Audio audio; double time = 0.5; size_t index = (time - audio.timeAtZero()) / audio.sampleRate(); int16_t value = audio[index]; double level = static_cast<double>(value) * audio.verticalScale();

slide-97
SLIDE 97

class Audio { public: size_t count() const; int16_t operator[](size_t i) const; int16_t& operator[](size_t i); double timeAtZero() const; double sampleRate() const; double verticalScale() const; };

slide-98
SLIDE 98

class Audio { public: size_t count() const; int16_t operator[](size_t i) const; int16_t& operator[](size_t i); double timeAtZero() const; double sampleRate() const; double verticalScale() const; std::string fileName() const; std::string comments() const; bool isStereo() const; void loadFromFile(const std::string& filename); void saveToFile(const std::string& filename); };

slide-99
SLIDE 99
slide-100
SLIDE 100

What is an audio clip?

slide-101
SLIDE 101
slide-102
SLIDE 102
slide-103
SLIDE 103

Sound changing
 continuously


  • ver time
slide-104
SLIDE 104

Sketch in Idris:

slide-105
SLIDE 105

Audio : (a : Type) -> Type Audio a = (Float -> a)

slide-106
SLIDE 106

Audio : (a : Type) -> Type Audio a = (Float -> Maybe a)

slide-107
SLIDE 107

template <typename T> class Audio { boost::optional<T> operator()(double t); };

slide-108
SLIDE 108

double time = 0.5; auto level = audio(time);

slide-109
SLIDE 109

How would we clip out an excerpt?

slide-110
SLIDE 110

clip : (Audio a) -> Float -> Float -> (Audio a)

slide-111
SLIDE 111

template <typename T> Audio<T> clip(Audio<T>, double, double);

slide-112
SLIDE 112

Mission:
 Define a matrix type

slide-113
SLIDE 113

template <typename T> class Matrix { public: T operator()(size_t row, size_t col); };

slide-114
SLIDE 114

Matrix<double> m(3, 5); size_t const x = 4; size_t const y = 0; cout << m(x, y) << endl; // wrong order!

slide-115
SLIDE 115

Denotational design

  • 1. Sketch your design in Idris
  • 2. Implement it in C++
slide-116
SLIDE 116

Matrix : Nat -> Nat -> Type -> Type get : Nat -> Nat -> Matrix rows cols a -> a

slide-117
SLIDE 117

Matrix : Nat -> Nat -> Type -> Type get : (Fin rows) -> (Fin cols) -> Matrix rows cols a -> a

slide-118
SLIDE 118

If only we had a finite number type in C++!

slide-119
SLIDE 119

template <size_t R, size_t C, typename T> class Matrix { public: T operator()(Fin<R> row, Fin<C> col); };

slide-120
SLIDE 120

Back to the drawing board

slide-121
SLIDE 121

Make the row and column
 two different types

slide-122
SLIDE 122

Matrix : Nat -> Nat -> Type -> Type get : (Fin rows) -> (Fin cols) -> Matrix rows cols a -> a

slide-123
SLIDE 123

data Row a = aRow a data Col a = aCol a Matrix : Nat -> Nat -> Type -> Type get : (Fin rows) -> (Fin cols) -> Matrix rows cols a -> a

slide-124
SLIDE 124

data Row a = aRow a data Col a = aCol a Matrix : Nat -> Nat -> Type -> Type get : (Fin rows) -> (Fin cols) -> Matrix rows cols a -> a

slide-125
SLIDE 125

data Row a = aRow a data Col a = aCol a Matrix : Nat -> Nat -> Type -> Type get : (Fin rows) -> (Fin cols) -> Matrix rows cols a -> a

slide-126
SLIDE 126

data Row a = aRow a data Col a = aCol a Matrix : Nat -> Nat -> Type -> Type get : Row (Fin rows) -> Col (Fin cols) -> Matrix rows cols a -> a

slide-127
SLIDE 127

class Row { public: explicit Row(size_t value) : value_(value) {}

  • perator size_t() { return value_; }

private: size_t value_; };

slide-128
SLIDE 128

class Row { public: explicit Row(size_t value) : value_(value) {}

  • perator size_t() { return value_; }

private: size_t value_; };

slide-129
SLIDE 129

class Row { public: explicit Row(size_t value) : value_(value) {}

  • perator size_t() { return value_; }

private: size_t value_; };

slide-130
SLIDE 130

template <typename T> class Matrix { public: T operator()(size_t row, size_t col); };

slide-131
SLIDE 131

template <typename T> class Matrix { public: T operator()(Row row, Col col); };

slide-132
SLIDE 132

Hard to mix up
 rows and columns!

slide-133
SLIDE 133

Further exploration

slide-134
SLIDE 134

The Intellectual Ascent to Agda

David Sankel
 C++Now 2013
 https://youtu.be/vy5C-mlUQ1w

slide-135
SLIDE 135

Enjoy the conference!

slide-136
SLIDE 136

Image credits

https://www.flickr.com/photos/hodgers/450003437 https://www.flickr.com/photos/jakerust/16649943478 https://www.flickr.com/photos/lendog64/5387445643 https://media2.giphy.com/media/EldfH1VJdbrwY/200.gif https://www.flickr.com/photos/ladybeames/2896787167 https://www.flickr.com/photos/jamesvela/12316917853

slide-137
SLIDE 137

Fine print

This document and the information herein (including any information that may be incorporated by reference) is provided for informational purposes only and should not be construed as an offer, commitment, promise or

  • bligation on behalf of New Relic, Inc. (“New Relic”) to sell securities or deliver any product, material, code,

functionality, or other feature. Any information provided hereby is proprietary to New Relic and may not be replicated or disclosed without New Relic’s express written permission. Such information may contain forward- looking statements within the meaning of federal securities laws. Any statement that is not a historical fact or refers to expectations, projections, future plans, objectives, estimates, goals, or other characterizations of future events is a forward-looking statement. These forward-looking statements can often be identified as such because the context of the statement will include words such as “believes,” “anticipates,” “expects” or words of similar

  • import. Actual results may differ materially from those expressed in these forward-looking statements, which speak
  • nly as of the date hereof, and are subject to change at any time without notice. Existing and prospective investors,

customers and other third parties transacting business with New Relic are cautioned not to place undue reliance on this forward-looking information. The achievement or success of the matters covered by such forward-looking statements are based on New Relic’s current assumptions, expectations, and beliefs and are subject to substantial risks, uncertainties, assumptions, and changes in circumstances that may cause the actual results, performance, or achievements to differ materially from those expressed or implied in any forward-looking statement. Further information on factors that could affect such forward-looking statements is included in the filings we make with the SEC from time to time. Copies of these documents may be obtained by visiting New Relic’s Investor Relations website at ir.newrelic.com or the SEC’s website at www.sec.gov. New Relic assumes no obligation and does not intend to update these forward-looking statements, except as required by law. New Relic makes no warranties, expressed or implied, in this document or otherwise, with respect to the information provided.