SLIDE 1 Misusing the Type System for &
Ian Dees • @undees PNSQC 2015
SLIDE 2
Brewing for Maintainability
SLIDE 3
Our contaminants:
SLIDE 4 M e m
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
Traditional remedies:
SLIDE 6
Code review Unit tests Exploratory testing “Just be careful”
SLIDE 7
Another proposal:
SLIDE 8
Lean on the type system
SLIDE 9
Agenda:
SLIDE 10
- 1. Ways you may already be using types
- 2. Denotational design
- 3. Applying the ideas in the real world
SLIDE 12
Memory leaks
SLIDE 13
Sandwich* makeSandwich(); Sandwich* sandwich = makeSandwich(); // who cleans up?
SLIDE 14
shared_ptr<Sandwich> makeSandwich(); auto sandwich = makeSandwich(); // cleanup is automatic!
SLIDE 15
$ find . -name \*.cpp | xargs grep new
SLIDE 16
Crashes
SLIDE 17
int findSandwich(string name); int found = findSandwich("cheese"); cout << Sandwiches[found] << endl;
SLIDE 18
// returns -1 if not found int findSandwich(string name); int found = findSandwich("cheese"); cout << Sandwiches[found] << endl;
SLIDE 19
- ptional<size_t> findSandwich(string name);
auto found = findSandwich("cheese"); cout << Sandwiches[found] << endl; // WON'T COMPILE! ^^^^^
SLIDE 20
- ptional<size_t> findSandwich(string name);
auto found = findSandwich("cheese"); if (found) { cout << Sandwiches[*found] << endl; }
SLIDE 21
Making errors
inexpressible
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
–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 In other words…
- 1. Sketch your design in clear notation
- 2. Implement it in your “day job” language
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
What kind of clear notation?
SLIDE 28
Math
(or a sufficiently math-y programming language)
SLIDE 29
Meet Idris
SLIDE 30
Haskell’s cousin
SLIDE 33
twice : Int -> Int twice x = 2 * x
SLIDE 34
twice : Int -> Int twice x = 2 * x twice 42 —-> 84 : Int
SLIDE 35
Dependently typed
SLIDE 36
Types can be inputs
to functions
SLIDE 37
- - Types can be inputs to functions
isNumeric : Type -> Bool isNumeric Int = True isNumeric String = False
SLIDE 38
- - Types can be inputs to functions
isNumeric : Type -> Bool isNumeric Int = True isNumeric String = False isNumeric Int
SLIDE 39 Types can depend
SLIDE 40
- - Idris, before we add dependent types
data Vec : Type -> Type where Nil : Vec a (::) : a -> Vec a -> Vec a
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
// C++ template <typename T> class vec { public: static vec<T> nil; vec<T> insert(T); };
SLIDE 43
data Vec : Nat -> Type -> Type where Nil : Vec Z a (::) : a -> Vec k a -> Vec (S k) a
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
(an aside on numbers)
SLIDE 46
Z --> 0 S Z --> 1 ("successor to zero") S (S Z) --> 2 ("successor to successor
to zero")
SLIDE 47
42
SLIDE 48
…is syntactic sugar for …
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
add : Vec n a -> Vec m a -> Vec (n + m) a
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 53
Proofs
SLIDE 54
add : Nat -> Nat -> Nat add Z y = y add (S x) y = S (add x y)
SLIDE 55
How would we check that addition is commutative? (x + y = y + x)
SLIDE 56
Tests?
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
Proofs are programs
SLIDE 60
- - Given natural numbers x and y,
- - x + y equals y + x
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
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
addCommutes Z y = ?base addCommutes (S x) y = let hypothesis = addCommutes x y in ?inductive
SLIDE 64
$ idris math.idr > :m Global metavariables: [Math.inductive,Math.base]
SLIDE 65 > :p base
- --------- Goal: ----------
{ hole 0 }: (y : Nat) -> y = plus y Z
SLIDE 66 > :p base
- --------- Goal: ----------
{ hole 0 }: (y : Nat) -> y = plus y Z
SLIDE 67 > intros
- --------- Other goals: ----------
{ hole 0 }
- --------- Assumptions: ----------
y : Nat
- --------- Goal: ----------
{ hole 1 }: y = plus y Z
SLIDE 68 > intros
- --------- Other goals: ----------
{ hole 0 }
- --------- Assumptions: ----------
y : Nat
- --------- Goal: ----------
{ hole 1 }: y = plus y Z
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 > rewrite (plusZeroRightNeutral y)
- --------- Other goals: ----------
{ hole 1 } { hole 0 }
- --------- Assumptions: ----------
y : Nat
- --------- Goal: ----------
{ hole 2 }: plus y Z = plus y Z
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 > trivial base: No more goals. > qed Proof completed! Math.base = proof intros rewrite (plusZeroRightNeutral y) trivial
SLIDE 73
And now , for the inductive step
SLIDE 74
> :m Global metavariables: [Math.inductive]
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 > :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 > 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 > 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
- - 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 > 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 > 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 > 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 > 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 > 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 > trivial inductive: No more goals. > qed Proof completed! Math.inductive = proof intros rewrite (plusSuccRightSucc y x) rewrite hypothesis trivial
SLIDE 86
SLIDE 87
in the real world
SLIDE 88
Mission:
Edit audio clips
SLIDE 89
SLIDE 90
class Audio { public: };
SLIDE 91
class Audio { public: size_t count() const; };
SLIDE 92
class Audio { public: size_t count() const; int16_t operator[](size_t i) const; int16_t& operator[](size_t i); };
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
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
Audio audio; double time = 0.5; size_t index = (time - audio.timeAtZero()) / audio.sampleRate();
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
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 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 100
What is an audio clip?
SLIDE 101
SLIDE 102
SLIDE 103 Sound changing
continuously
SLIDE 104
Sketch in Idris:
SLIDE 105
Audio : (a : Type) -> Type Audio a = (Float -> a)
SLIDE 106
Audio : (a : Type) -> Type Audio a = (Float -> Maybe a)
SLIDE 107
template <typename T> class Audio { boost::optional<T> operator()(double t); };
SLIDE 108
double time = 0.5; auto level = audio(time);
SLIDE 109
How would we clip out an excerpt?
SLIDE 110
clip : (Audio a) -> Float -> Float -> (Audio a)
SLIDE 111
template <typename T> Audio<T> clip(Audio<T>, double, double);
SLIDE 112
Mission:
Define a matrix type
SLIDE 113
template <typename T> class Matrix { public: T operator()(size_t row, size_t col); };
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 Denotational design
- 1. Sketch your design in Idris
- 2. Implement it in C++
SLIDE 116
Matrix : Nat -> Nat -> Type -> Type get : Nat -> Nat -> Matrix rows cols a -> a
SLIDE 117
Matrix : Nat -> Nat -> Type -> Type get : (Fin rows) -> (Fin cols) -> Matrix rows cols a -> a
SLIDE 118
If only we had a finite number type in C++!
SLIDE 119
template <size_t R, size_t C, typename T> class Matrix { public: T operator()(Fin<R> row, Fin<C> col); };
SLIDE 120
Back to the drawing board
SLIDE 121
Make the row and column
two different types
SLIDE 122
Matrix : Nat -> Nat -> Type -> Type get : (Fin rows) -> (Fin cols) -> Matrix rows cols a -> a
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
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
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
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 class Row { public: explicit Row(size_t value) : value_(value) {}
- perator size_t() { return value_; }
private: size_t value_; };
SLIDE 128 class Row { public: explicit Row(size_t value) : value_(value) {}
- perator size_t() { return value_; }
private: size_t value_; };
SLIDE 129 class Row { public: explicit Row(size_t value) : value_(value) {}
- perator size_t() { return value_; }
private: size_t value_; };
SLIDE 130
template <typename T> class Matrix { public: T operator()(size_t row, size_t col); };
SLIDE 131
template <typename T> class Matrix { public: T operator()(Row row, Col col); };
SLIDE 132
Hard to mix up
rows and columns!
SLIDE 133
Further exploration
SLIDE 134 The Intellectual Ascent to Agda
David Sankel
C++Now 2013
https://youtu.be/vy5C-mlUQ1w
SLIDE 135
Enjoy the conference!
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 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.