├── .gitignore ├── function.svg ├── function_bool.svg ├── int_bool_1.png ├── int_bool_2.png ├── presentation.css ├── presentation.js ├── presentation.org ├── submission.org └── variant-tweet.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /function.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 34 | 35 | 43 | 48 | 49 | 57 | 62 | 63 | 71 | 76 | 77 | 85 | 91 | 92 | 93 | 112 | 114 | 115 | 117 | image/svg+xml 118 | 120 | 121 | 122 | 123 | 124 | 129 | 136 | 143 | 150 | 157 | 164 | 176 | 188 | 195 | 201 | 207 | 213 | 219 | 225 | 231 | 236 | 241 | 247 | 253 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /function_bool.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 34 | 35 | 44 | 49 | 50 | 58 | 63 | 64 | 72 | 77 | 78 | 86 | 91 | 92 | 100 | 105 | 106 | 114 | 120 | 121 | 129 | 135 | 136 | 144 | 150 | 151 | 159 | 165 | 166 | 174 | 180 | 181 | 189 | 195 | 196 | 204 | 210 | 211 | 219 | 225 | 226 | 234 | 240 | 241 | 249 | 255 | 256 | 257 | 276 | 278 | 279 | 281 | image/svg+xml 282 | 284 | 285 | 286 | 287 | 288 | 293 | 300 | 312 | 319 | 325 | 332 | 344 | 351 | 357 | 363 | 369 | 376 | 388 | 395 | 401 | 407 | 413 | 420 | 432 | 439 | 445 | 451 | 457 | 464 | 476 | 483 | 489 | 495 | 501 | 508 | 520 | 527 | 533 | 539 | 545 | 552 | 564 | 571 | 577 | 583 | 589 | 596 | 608 | 615 | 621 | 627 | 633 | 640 | 652 | 659 | 665 | 671 | 676 | 681 | 686 | 691 | 696 | 701 | 706 | 711 | 722 | f1 733 | f2 744 | f3 755 | f4 766 | 767 | 768 | -------------------------------------------------------------------------------- /int_bool_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbeno/using-types-effectively/1b5c61f7013d228e1207cf05c61e51db50f042eb/int_bool_1.png -------------------------------------------------------------------------------- /int_bool_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbeno/using-types-effectively/1b5c61f7013d228e1207cf05c61e51db50f042eb/int_bool_2.png -------------------------------------------------------------------------------- /presentation.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Inconsolata"; 3 | font-style: normal; 4 | font-weight: normal; 5 | src: local("Inconsolata"), url("http://themes.googleusercontent.com/static/fonts/inconsolata/v3/BjAYBlHtW3CJxDcjzrnZCIbN6UDyHWBl620a-IRfuBk.woff") format("woff"); 6 | } 7 | 8 | .reveal code { 9 | font-family: Inconsolata; 10 | } 11 | 12 | .reveal a { 13 | text-transform: initial; 14 | } 15 | .reveal pre { 16 | font-family: Inconsolata; 17 | font-size: 30px; 18 | } 19 | 20 | #fiveup code { 21 | font-family: Inconsolata; 22 | font-size: 20px; 23 | } 24 | 25 | #columns ul { 26 | width:1300px; 27 | overflow:hidden; 28 | } 29 | #columns li { 30 | float:left; 31 | display:inline; 32 | } 33 | 34 | #double li { width:50%; } 35 | #triple li { width:33.333%; } 36 | #fourup li { width:25%; } 37 | #fiveup li { width:20%; } 38 | #sixup li { width:16.666%; } 39 | 40 | #achv * { 41 | -webkit-box-sizing: border-box; 42 | -moz-box-sizing: border-box; 43 | box-sizing: border-box; 44 | } 45 | 46 | #achv .achievement-banner { 47 | opacity: 0; 48 | margin: 0 auto; 49 | font-size: 350%; 50 | width: 10em; 51 | color: #efefef; 52 | background: #3d4142; 53 | border-radius: 10em; 54 | position: relative; 55 | padding: .125em; 56 | min-height: 1.5em; 57 | overflow: hidden; 58 | } 59 | .achievement-banner .achievement-icon { 60 | border-radius: 10em; 61 | position: relative; 62 | background: #000; 63 | border: .125em solid #656766; 64 | border-top-color: #45ca3d; 65 | height: 1.25em; 66 | width: 1.25em; 67 | -webkit-transform: rotate(-45deg); 68 | -moz-transform: rotate(-45deg); 69 | transform: rotate(-45deg); 70 | z-index: 100; 71 | } 72 | .achievement-banner .achievement-icon .icon { 73 | display: block; 74 | border-radius: 10em; 75 | border: 4px solid #000; 76 | text-align: center; 77 | position: absolute; 78 | top: 0; 79 | left: 0; 80 | width: 1em; 81 | height: 1em; 82 | line-height: 1; 83 | z-index: 1000; 84 | -webkit-transform: rotate(45deg); 85 | -moz-transform: rotate(45deg); 86 | transform: rotate(45deg); 87 | } 88 | .achievement-banner .achievement-icon .icon .icon-trophy { 89 | font-size: 75%; 90 | line-height: 1; 91 | position: relative; 92 | top: -.08em; 93 | } 94 | .achievement-banner .achievement-text { 95 | font-family: "Trebuchet MS"; 96 | text-shadow: 0 2px 0 #000; 97 | position: absolute; 98 | top: .1em; 99 | left: 4em; 100 | font-size: 50%; 101 | line-height: 1; 102 | width: 70%; 103 | } 104 | .achievement-banner .achievement-notification { 105 | margin: 0; 106 | } 107 | .achievement-banner .achievement-name { 108 | margin: 0; 109 | } 110 | 111 | /* Generated content */ 112 | .achievement-banner .achievement-icon:before, 113 | .achievement-banner .achievement-icon:after, 114 | .achievement-banner .achievement-icon .icon:before, 115 | .achievement-banner .achievement-icon .icon:after { 116 | content: ""; 117 | display: block; 118 | position: absolute; 119 | } 120 | 121 | .achievement-banner .achievement-icon:before{ 122 | border-radius: 12em; 123 | z-index: -100; 124 | background: transparent; 125 | border: 4px solid #000; 126 | top: -.175em; 127 | left: -.175em; 128 | right: -.175em; 129 | bottom: -.175em; 130 | -webkit-transform: rotate(45deg); 131 | -moz-transform: rotate(45deg); 132 | transform: rotate(45deg); 133 | } 134 | /* Bars */ 135 | .achievement-banner .achievement-icon .icon:before, 136 | .achievement-banner .achievement-icon .icon:after { 137 | width: 150%; 138 | height: .125em; 139 | background: #000; 140 | border-radius: .05em; 141 | z-index: -10; 142 | top: 42.5%; 143 | left: -25%; 144 | } 145 | 146 | .achievement-banner .achievement-icon .icon:before { 147 | -webkit-transform: rotate(90deg); 148 | -moz-transform: rotate(90deg); 149 | transform: rotate(90deg); 150 | } 151 | 152 | /* Animations */ 153 | 154 | /* open-close-banner */ 155 | @-webkit-keyframes open-close-banner { 156 | 0% {width: 1.5em; opacity: 0;} 157 | 2% {width: 1.5em; opacity: 1;} 158 | 8% {width: 9em;} 159 | 80% {width: 9em;} 160 | 90% {width: 1.5em; opacity: 1; 161 | -webkit-transform: rotate(0deg); 162 | } 163 | 98% { 164 | width: 1.5em; 165 | -webkit-transform: rotate(720deg); 166 | } 167 | 100% {width: 1.5em; opacity: 0;} 168 | } 169 | @-moz-keyframes open-close-banner { 170 | 0% {width: 1.5em; opacity: 0;} 171 | 2% {width: 1.5em; opacity: 1;} 172 | 8% {width: 9em;} 173 | 80% {width: 9em;} 174 | 90% { 175 | width: 1.5em; 176 | opacity: 1; 177 | -moz-transform: rotate(0deg); 178 | } 179 | 98% { 180 | width: 1.5em; 181 | -moz-transform: rotate(720deg); 182 | } 183 | 100% {width: 1.5em; opacity: 0;} 184 | } 185 | @keyframes open-close-banner { 186 | 0% {width: 1.5em; opacity: 0;} 187 | 2% {width: 1.5em; opacity: 1;} 188 | 8% {width: 9em;} 189 | 80% {width: 9em;} 190 | 90% { 191 | width: 1.5em; 192 | opacity: 1; 193 | transform: rotate(0deg); 194 | } 195 | 98% { 196 | width: 1.5em; 197 | transform: rotate(720deg); 198 | } 199 | 100% {width: 1.5em; opacity: 0;} 200 | } 201 | 202 | /* border-top-pulse */ 203 | @-webkit-keyframes border-top-pulse { 204 | 0% {border-top-color: #656766;} 205 | 40% {border-top-color: #ffffff;} 206 | 80% {border-top-color: #00ff00;} 207 | 100% {border-top-color: #45ca3d;} 208 | } 209 | @-moz-keyframes border-top-pulse { 210 | 0% {border-top-color: #656766;} 211 | 40% {border-top-color: #ffffff;} 212 | 80% {border-top-color: #00ff00;} 213 | 100% {border-top-color: #45ca3d;} 214 | } 215 | @keyframes border-top-pulse { 216 | 0% {border-top-color: #656766;} 217 | 40% {border-top-color: #ffffff;} 218 | 80% {border-top-color: #00ff00;} 219 | 100% {border-top-color: #45ca3d;} 220 | } 221 | @-webkit-keyframes fade-in-text { 222 | 0% {opacity: 0;} 223 | 80% {opacity: 0;} 224 | 100% {opacity: 1;} 225 | } 226 | @-moz-keyframes fade-in-text { 227 | 0% {opacity: 0;} 228 | 80% {opacity: 0;} 229 | 100% {opacity: 1;} 230 | } 231 | @keyframes fade-in-text { 232 | 0% {opacity: 0;} 233 | 80% {opacity: 0;} 234 | 100% {opacity: 1;} 235 | } 236 | @-webkit-keyframes fade-out-text { 237 | 0% {opacity: 1;} 238 | 65% {opacity: 1;} 239 | 100% {opacity: 0;} 240 | } 241 | @-moz-keyframes fade-out-text { 242 | 0% {opacity: 1;} 243 | 65% {opacity: 1;} 244 | 100% {opacity: 0;} 245 | } 246 | @keyframes fade-out-text { 247 | 0% {opacity: 1;} 248 | 65% {opacity: 1;} 249 | 100% {opacity: 0;} 250 | } 251 | 252 | .achievement-banner { 253 | -webkit-animation: open-close-banner 5s 1; 254 | -moz-animation: open-close-banner 5s 1; 255 | animation: open-close-banner 5s 1; 256 | } 257 | 258 | .achievement-banner .achievement-icon { 259 | /* Make border-top pulse. */ 260 | -webkit-animation: border-top-pulse 1.25s 2; 261 | -moz-animation: border-top-pulse 1.25s 2; 262 | animation: border-top-pulse 1.25s 2; 263 | } 264 | /* fade-in-text */ 265 | .achievement-banner .achievement-text { 266 | -webkit-animation: fade-in-text .5s 1; 267 | -moz-animation: fade-in-text .5s 1; 268 | animation: fade-in-text .5s 1; 269 | } 270 | 271 | .achievement-banner .icon-trophy { 272 | -webkit-animation: fade-out-text 5s 1; 273 | -moz-animation: fade-out-text 5s 1; 274 | animation: fade-out-text 5s 1; 275 | } 276 | -------------------------------------------------------------------------------- /presentation.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | 'use strict'; 3 | 4 | window.addEventListener('load', function () { 5 | window.Reveal.addEventListener('slidechanged', function (event) { 6 | var achv = document.getElementById("achv"), 7 | achvParent = achv.parentNode, 8 | newachv = {}; 9 | // event.previousSlide, event.currentSlide, event.indexh, event.indexv 10 | if (event.indexv === 37) { 11 | newachv = achv.cloneNode(true); 12 | achvParent.removeChild(achv); 13 | setTimeout(function() { 14 | achvParent.appendChild(newachv); }, 500); 15 | } 16 | window.Reveal.pause(); 17 | }); 18 | }); 19 | 20 | })(window); 21 | -------------------------------------------------------------------------------- /presentation.org: -------------------------------------------------------------------------------- 1 | # -*- mode: org -*- 2 | #+OPTIONS: reveal_center:t reveal_progress:t reveal_history:t reveal_control:nil 3 | #+OPTIONS: reveal_rolling_links:nil reveal_keyboard:t reveal_overview:t num:nil 4 | #+OPTIONS: reveal_width:1200 reveal_height:900 5 | #+OPTIONS: toc:nil <:nil timestamp:nil email:t reveal_slide_number:"c/t" 6 | #+REVEAL_MARGIN: 0.1 7 | #+REVEAL_MIN_SCALE: 0.5 8 | #+REVEAL_MAX_SCALE: 2.5 9 | #+REVEAL_TRANS: none 10 | #+REVEAL_THEME: solarized 11 | #+REVEAL_HLEVEL: 1 12 | #+REVEAL_EXTRA_CSS: ./presentation.css 13 | #+REVEAL_ROOT: ../reveal.js/ 14 | 15 | # (setq org-reveal-title-slide "

%t


%a

%e / @ben_deane

%d

") 16 | # (setq org-reveal-title-slide 'auto) 17 | # see https://github.com/yjwen/org-reveal/commit/84a445ce48e996182fde6909558824e154b76985 18 | 19 | #+TITLE: Using Types Effectively 20 | #+AUTHOR: Ben Deane 21 | #+EMAIL: bdeane@blizzard.com 22 | #+DATE: September 19th, 2016 23 | 24 | #+REVEAL_HTML: 25 | * Using Types Effectively? 26 | ** What does that mean? 27 | #+REVEAL_HTML:
28 | The recent evolution of C++ is (from one point of view) largely about 29 | *strengthening* and *expanding* the *capabilities for dealing with types*. 30 | 31 | #+ATTR_REVEAL: :frag (appear) 32 | - expansion of ~type_traits~ 33 | - ~decltype~ to utter types 34 | - ~auto~ to preserve types, prevent conversions, infer return types 35 | - ~nullptr~ to prevent ~int~ / pointer confusion 36 | - scoped ~enum~ 37 | - GSL: ~owner~, ~not_null~ 38 | - Concepts TS 39 | 40 | #+BEGIN_NOTES 41 | Some of the features added to modern C++ to deal with types. 42 | 43 | Obviously someone thinks that types are important. And the ability to wrangle 44 | them and mould them to our purposes is an important part of C++. 45 | 46 | Types are the currency of metaprogramming, but also important for safety in 47 | "regular" programming; hence we see things like the GSL's ~owner<>~ and ~not_null<>~. 48 | #+END_NOTES 49 | 50 | ** FP isn't (only) about 51 | #+REVEAL_HTML:
52 | #+ATTR_REVEAL: :frag (appear) 53 | - first class functions 54 | - higher order functions 55 | - lexical scoping, closures 56 | - pattern matching 57 | - value semantics 58 | - immutability 59 | - concurrency through immutability 60 | - laziness 61 | - garbage collection 62 | - boxed data types / "inefficient" runtime models 63 | - the M-word 64 | 65 | #+BEGIN_NOTES 66 | Functional languages can teach us a thing or two about types. (After all, they 67 | seem to be teaching us everything else.) 68 | 69 | Here are some things you may think of when functional programming is mentioned... 70 | #+END_NOTES 71 | 72 | ** FP is (also, importantly) about 73 | #+REVEAL_HTML:
74 | #+ATTR_REVEAL: :frag (appear) 75 | - using types effectively and expressively 76 | - making illegal states unrepresentable 77 | - making illegal behaviour result in a type error 78 | - using total functions for easier to use, harder to misuse interfaces 79 | 80 | #+BEGIN_NOTES 81 | These aspects of functional programming are often overlooked. 82 | 83 | Many functional languages have well-developed, expressive type systems. 84 | 85 | C++ is moving in that direction. 86 | #+END_NOTES 87 | 88 | ** Why does C++ have a type system? :noexport: 89 | #+REVEAL_HTML:
90 | #+ATTR_REVEAL: :frag (appear appear appear appear) :frag_idx (1 2 3 4) 91 | - to help programmers? 92 | - to hinder programmers? 93 | - because objects? 94 | - for the compiler's benefit? 95 | #+REVEAL_HTML:

96 | #+ATTR_REVEAL: :frag appear :frag_idx 5 97 | What has a type system ever done for us? 98 | 99 | #+BEGIN_NOTES 100 | C++ has a stronger type system than C. Why? 101 | 102 | Is it so the compiler can complain about signed/unsigned comparisons? 103 | 104 | What use is a type system? This is the question this talk will help to answer. 105 | #+END_NOTES 106 | 107 | ** Why does C have a type system? :noexport: 108 | #+REVEAL_HTML:
109 | "The machines on which we first used BCPL and then B were word-addressed, and 110 | these languages' single data type, the 'cell,' comfortably equated with the 111 | hardware machine word. The advent of the PDP-11 exposed several inadequacies of 112 | B's semantic model. 113 | 114 | First, its character-handling mechanisms were clumsy. Second, although the 115 | original PDP-11 did not provide for floating-point arithmetic, the manufacturer 116 | promised that it would soon be available. Finally, the B and BCPL model implied 117 | overhead in dealing with pointers. 118 | 119 | For all these reasons, it seemed that a typing scheme was necessary. Other 120 | issues, particularly type safety and interface checking, did not seem as 121 | important then as they became later." 122 | 123 | #+REVEAL_HTML:
124 | -- dmr, [[https://www.bell-labs.com/usr/dmr/www/chist.html][/The Development of the C Language/]] 125 | #+REVEAL_HTML:
126 | 127 | 128 | The PDP-10 was old, with its 36-bit words. 129 | 130 | 1971/72: The PDP-11 was the new hotness. 131 | 132 | It could operate on (8-bit) bytes and (16-bit) words. If your language only 133 | operates on words ('cells'), string/char handling is awkward. 134 | 135 | In B, pointers were indices into arrays rather than naked addresses. So scale 136 | conversion would be needed at runtime. 137 | 138 | DEC promised floating point capability in hardware! So the C compiler would need 139 | to know about types in order to output the correct instructions. 140 | #+END_NOTES 141 | 142 | ** What is a type? 143 | #+REVEAL_HTML:
144 | #+ATTR_REVEAL: :frag (appear) 145 | - A way for the compiler to know what opcodes to output (dmr's motivation)? 146 | - The way data is stored (representational)? 147 | - Characterised by what operations are possible (behavioural)? 148 | - Determines the values that can be assigned? 149 | - Determines the meaning of the data? 150 | 151 | ** What is a type? 152 | [[./int_bool_1.png]] 153 | 154 | "Only Lua would have '~1 == true~' evaluate to ~false~. #wantmydayback" 155 | 156 | #+REVEAL_HTML:
157 | [[./int_bool_2.png]] 158 | 159 | "But, how can ~1~ be equal to ~true~? ~1~ is an integer, and ~true~ is a boolean. Lua 160 | seems to be correct here. It's your view of the world that has been warped." 161 | 162 | (Smiley faces make criticism OK!) 163 | #+REVEAL_HTML:
164 | 165 | ** What is a type? 166 | #+REVEAL_HTML:
167 | #+ATTR_REVEAL: :frag (appear) 168 | - The set of values that can inhabit an expression 169 | - may be finite or "infinite" 170 | - characterized by cardinality 171 | - Expressions have types 172 | - A program has a type 173 | 174 | ** Let's play a game 175 | #+ATTR_REVEAL: :frag appear 176 | To help us get thinking about types. 177 | 178 | #+ATTR_REVEAL: :frag appear 179 | I'll tell you a type. 180 | 181 | #+ATTR_REVEAL: :frag appear 182 | You tell me how many values it has. 183 | 184 | #+ATTR_REVEAL: :frag appear 185 | There are no tricks: if it seems obvious, it is! 186 | 187 | ** Level 1 188 | #+REVEAL_HTML:
189 | Types as sets of values 190 | 191 | ** Level 1 192 | How many values? 193 | #+BEGIN_SRC cpp 194 | bool; 195 | #+END_SRC 196 | 197 | #+ATTR_REVEAL: :frag appear 198 | 2 (~true~ and ~false~) 199 | 200 | ** Level 1 201 | How many values? 202 | #+BEGIN_SRC cpp 203 | char; 204 | #+END_SRC 205 | 206 | #+ATTR_REVEAL: :frag appear 207 | 256 208 | 209 | ** Level 1 210 | How many values? 211 | #+BEGIN_SRC cpp 212 | void; 213 | #+END_SRC 214 | 215 | #+ATTR_REVEAL: :frag appear 216 | 0 217 | 218 | #+ATTR_REVEAL: :frag appear 219 | #+BEGIN_SRC cpp 220 | struct Foo { Foo() = delete; }; 221 | #+END_SRC 222 | 223 | #+ATTR_REVEAL: :frag appear 224 | #+BEGIN_SRC cpp 225 | struct Bar { template Bar(); }; 226 | #+END_SRC 227 | 228 | #+BEGIN_NOTES 229 | cf BASIC's function vs procedure 230 | #+END_NOTES 231 | 232 | ** Level 1 233 | How many values? 234 | #+BEGIN_SRC cpp 235 | struct Foo {}; 236 | #+END_SRC 237 | 238 | #+ATTR_REVEAL: :frag appear 239 | 1 240 | 241 | ** Level 1 242 | How many values? 243 | #+BEGIN_SRC cpp 244 | enum FireSwampDangers : int8_t { 245 | FLAME_SPURTS, 246 | LIGHTNING_SAND, 247 | ROUSES 248 | }; 249 | #+END_SRC 250 | 251 | #+ATTR_REVEAL: :frag appear 252 | 3 253 | 254 | #+BEGIN_NOTES 255 | It is possible to put something into FireSwampDangers that fits 256 | representationally (eg. the value 4). But that would be meaningless: there would 257 | be no connection between the value represented and its interpretation. Because 258 | there is no interpretation: cf. an unconstructed object. 259 | #+END_NOTES 260 | 261 | ** Level 1 262 | How many values? 263 | #+BEGIN_SRC cpp 264 | template 265 | struct Foo { 266 | T m_t; 267 | }; 268 | #+END_SRC 269 | 270 | #+ATTR_REVEAL: :frag appear 271 | ~Foo~ has as many values as ~T~ 272 | 273 | ** End of Level 1 274 | Algebraically, a type is the number of values that inhabit it. 275 | 276 | These types are equivalent: 277 | #+BEGIN_SRC cpp 278 | bool; 279 | 280 | enum class InatorButtons { 281 | ON_OFF, 282 | SELF_DESTRUCT 283 | }; 284 | #+END_SRC 285 | 286 | #+ATTR_REVEAL: :frag appear 287 | Let's move on to level 2. 288 | 289 | ** Level 2 290 | #+REVEAL_HTML:
291 | Aggregating Types 292 | 293 | ** Level 2 294 | How many values? 295 | #+BEGIN_SRC cpp 296 | std::pair; 297 | #+END_SRC 298 | 299 | #+ATTR_REVEAL: :frag appear 300 | 256 * 2 = 512 301 | 302 | ** Level 2 303 | How many values? 304 | #+BEGIN_SRC cpp 305 | struct Foo { 306 | char a; 307 | bool b; 308 | }; 309 | #+END_SRC 310 | 311 | #+ATTR_REVEAL: :frag appear 312 | 256 * 2 = 512 313 | 314 | ** Level 2 315 | How many values? 316 | #+BEGIN_SRC cpp 317 | std::tuple; 318 | #+END_SRC 319 | 320 | #+ATTR_REVEAL: :frag appear 321 | 2 * 2 * 2 = 8 322 | 323 | ** Level 2 324 | How many values? 325 | #+BEGIN_SRC cpp 326 | template 327 | struct Foo { 328 | T m_t; 329 | U m_u; 330 | }; 331 | #+END_SRC 332 | 333 | #+ATTR_REVEAL: :frag appear 334 | (# of values in ~T~) * (# of values in ~U~) 335 | 336 | ** End of Level 2 337 | When two types are "concatenated" into one compound type, we _multiply_ the # of 338 | inhabitants of the components. 339 | 340 | This kind of compounding gives us a _product type_. 341 | 342 | #+ATTR_REVEAL: :frag appear 343 | On to Level 3. 344 | 345 | ** Level 3 346 | #+REVEAL_HTML:
347 | Alternating Types 348 | 349 | ** Level 3 350 | How many values? 351 | #+BEGIN_SRC cpp 352 | std::optional; 353 | #+END_SRC 354 | 355 | #+ATTR_REVEAL: :frag appear 356 | 256 + 1 = 257 357 | 358 | ** Level 3 359 | How many values? 360 | #+BEGIN_SRC cpp 361 | std::variant; 362 | #+END_SRC 363 | 364 | #+ATTR_REVEAL: :frag appear 365 | 256 + 2 = 258 366 | 367 | ** Level 3 368 | How many values? 369 | #+BEGIN_SRC cpp 370 | template 371 | struct Foo { 372 | std::variant; 373 | } 374 | #+END_SRC 375 | 376 | #+ATTR_REVEAL: :frag appear 377 | (# of values in ~T~) + (# of values in ~U~) 378 | 379 | ** End of Level 3 380 | When two types are "alternated" into one compound type, we _add_ the # of 381 | inhabitants of the components. 382 | 383 | This kind of compounding gives us a _sum type_. 384 | 385 | ** Level 4 386 | #+REVEAL_HTML:
387 | Function Types 388 | 389 | ** Level 4 390 | How many values? 391 | #+begin_src c++ 392 | bool f(bool); 393 | #+end_src 394 | 395 | #+ATTR_REVEAL: :frag appear 396 | 4 397 | 398 | ** Level 4 399 | Four possible values 400 | [[./function_bool.svg]] 401 | 402 | ** Level 4 403 | #+begin_src c++ 404 | bool f1(bool b) { return b; } 405 | bool f2(bool) { return true; } 406 | bool f3(bool) { return false; } 407 | bool f4(bool b) { return !b; } 408 | #+end_src 409 | 410 | ** Level 4 411 | How many values? 412 | #+begin_src c++ 413 | char f(bool); 414 | #+end_src 415 | 416 | #+ATTR_REVEAL: :frag appear 417 | 256 * 256 = 65,536 418 | 419 | ** Level 4 420 | How many values (for ~f~)? 421 | #+begin_src c++ 422 | enum class Foo 423 | { 424 | BAR, 425 | BAZ, 426 | QUUX 427 | }; 428 | char f(Foo); 429 | #+end_src 430 | 431 | #+ATTR_REVEAL: :frag appear 432 | 256 * 256 * 256 = 16,777,216 433 | 434 | ** Level 4 435 | The number of values of a function is the number of different ways we can draw 436 | arrows between the inputs and the outputs. 437 | [[./function.svg]] 438 | 439 | ** Level 4 440 | How many values? 441 | #+begin_src c++ 442 | template 443 | U f(T); 444 | #+end_src 445 | 446 | #+ATTR_REVEAL: :frag appear 447 | $|U|^{|T|}$ 448 | 449 | ** End of Level 4 450 | When we have a _function_ from $A$ to $B$, we raise the # of inhabitants of 451 | $B$ to the power of the # of inhabitants of $A$. 452 | 453 | ** End of Level 4 (corollary) 454 | Hence a curried function is equivalent to its uncurried alternative. 455 | 456 | 457 | $$\begin{align*} 458 | F_{uncurried}::(A,B) \rightarrow C & \Leftrightarrow C^{A*B} \\ 459 | & = C^{B*A} \\ 460 | & = (C^B)^A \\ 461 | & \Leftrightarrow (B \rightarrow C)^A \\ 462 | & \Leftrightarrow F_{curried}::A \rightarrow (B \rightarrow C) 463 | \end{align*}$$ 464 | 465 | ** Victory! 466 | 467 | #+REVEAL_HTML:

🏆

ACHIEVEMENT UNLOCKED

Algebraic Datatypes 101

468 | 469 | ** Equivalences 470 | #+BEGIN_SRC cpp 471 | template 472 | struct Foo { 473 | std::variant m_v; 474 | }; 475 | 476 | template 477 | struct Bar { 478 | T m_t; 479 | bool m_b; 480 | }; 481 | #+END_SRC 482 | 483 | We have a choice over how to represent values. ~std::variant~ will quickly 484 | become a very important tool for proper expression of states. 485 | 486 | This is one reason why ~std::variant~'s "never-empty" guarantee is important. 487 | 488 | #+BEGIN_NOTES 489 | T + T = 2T. 490 | 491 | But note that in ~Bar~, we need to manually keep the two variables "in sync". 492 | #+END_NOTES 493 | 494 | ** Algebraic Datatypes 495 | This is what it means to have an algebra of datatypes. 496 | 497 | #+ATTR_REVEAL: :frag (appear) 498 | - the ability to reason about equality of types 499 | - to find equivalent formulations 500 | - more natural 501 | - more easily understood 502 | - more efficient 503 | - to identify mismatches between state spaces and the types used to implement 504 | them 505 | - to eliminate illegal states by making them inexpressible 506 | 507 | ** Making Illegal States Unrepresentable 508 | ~std::variant~ is a game changer because it allows us to (more) properly express 509 | types, so that (more) illegal states are unrepresentable. 510 | 511 | [[./variant-tweet.png]] 512 | 513 | #+BEGIN_NOTES 514 | C++'s type system is still not perfect by a long shot. But ~std::variant~ is an 515 | amazing upgrade. 516 | #+END_NOTES 517 | 518 | ** Making Illegal States Unrepresentable 519 | Let's look at some possible alternative data formulations, using sum types 520 | (~variant~, ~optional~) as well as product types (structs). 521 | 522 | ** Example: Connection State 523 | #+BEGIN_SRC cpp 524 | enum class ConnectionState { 525 | DISCONNECTED, 526 | CONNECTING, 527 | CONNECTED, 528 | CONNECTION_INTERRUPTED 529 | }; 530 | 531 | struct Connection { 532 | ConnectionState m_connectionState; 533 | 534 | std::string m_serverAddress; 535 | ConnectionId m_id; 536 | std::chrono::system_clock::time_point m_connectedTime; 537 | std::chrono::milliseconds m_lastPingTime; 538 | Timer m_reconnectTimer; 539 | }; 540 | #+END_SRC 541 | 542 | #+BEGIN_NOTES 543 | A very simple example of what a connection class might look like today. 544 | 545 | Functions interacting with this class would typically use a switch statement 546 | over the ~ConnectionState~. 547 | 548 | There are hidden invariants here that aren't enforced by the Connection type. 549 | 550 | Some of the fields are dependent on the connection state (reconnect time, last 551 | ping time). So it seems that some of these fields need sentinel values (eg 552 | invalid connection id). 553 | 554 | Worse, there is temptation to reuse fields for multiple states. Connected 555 | timestamp is perhaps likely to get reused to mean the instant of connection and 556 | the instant of disconnection. 557 | #+END_NOTES 558 | 559 | ** Example: Connection State 560 | #+BEGIN_SRC cpp 561 | struct Connection { 562 | std::string m_serverAddress; 563 | 564 | struct Disconnected {}; 565 | struct Connecting {}; 566 | struct Connected { 567 | ConnectionId m_id; 568 | std::chrono::system_clock::time_point m_connectedTime; 569 | std::optional m_lastPingTime; 570 | }; 571 | struct ConnectionInterrupted { 572 | std::chrono::system_clock::time_point m_disconnectedTime; 573 | Timer m_reconnectTimer; 574 | }; 575 | 576 | std::variant m_connection; 580 | }; 581 | #+END_SRC 582 | 583 | #+BEGIN_NOTES 584 | With types structured correctly, it's not possible to express illegal states. 585 | 586 | e.g. Ping time does not exist if we're not connected. 587 | 588 | (There are still things that are common to all states, e.g. perhaps this class 589 | represents connection to a specific server.) 590 | 591 | A switch statement could still exist, switching on the ~variant~'s ~index()~, or 592 | a visitor-based approach could be used. 593 | #+END_NOTES 594 | 595 | ** Example: Nullable field 596 | #+REVEAL_HTML:
597 | #+BEGIN_SRC cpp 598 | class Friend { 599 | std::string m_alias; 600 | bool m_aliasPopulated; 601 | ... 602 | }; 603 | #+END_SRC 604 | These two fields need to be kept in sync everywhere. 605 | 606 | #+BEGIN_NOTES 607 | Here, a field is populated from a remote source and happens lazily and/or 608 | asynchronously. It is possible that the field never gets populated. 609 | 610 | All the code that deals with this field has to ensure that both variables are 611 | kept up to date in sync with each other. 612 | #+END_NOTES 613 | 614 | ** Example: Nullable field 615 | #+REVEAL_HTML:
616 | #+BEGIN_SRC cpp 617 | class Friend { 618 | std::optional m_alias; 619 | ... 620 | }; 621 | #+END_SRC 622 | ~std::optional~ provides a sentinel value that is outside the type. 623 | 624 | #+BEGIN_NOTES 625 | ~std::optional~ captures the true state space of the variable. It is not 626 | possible for two fields to get out of step now. 627 | #+END_NOTES 628 | 629 | ** Example: Monster AI 630 | #+REVEAL_HTML:
631 | #+BEGIN_SRC cpp 632 | enum class AggroState { 633 | IDLE, 634 | CHASING, 635 | FIGHTING 636 | }; 637 | 638 | class MonsterAI { 639 | AggroState m_aggroState; 640 | 641 | float m_aggroRadius; 642 | PlayerId m_target; 643 | Timer m_chaseTimer; 644 | }; 645 | #+END_SRC 646 | 647 | #+BEGIN_NOTES 648 | Once again, presumably PlayerId has some invalid sentinel value. 649 | #+END_NOTES 650 | 651 | ** Example: Monster AI 652 | #+REVEAL_HTML:
653 | #+BEGIN_SRC cpp 654 | class MonsterAI { 655 | struct Idle { 656 | float m_aggroRadius; 657 | }; 658 | struct Chasing { 659 | PlayerId m_target; 660 | Timer m_chaseTimer; 661 | }; 662 | struct Fighting { 663 | PlayerId m_target; 664 | }; 665 | 666 | std::variant m_aggroState; 667 | }; 668 | #+END_SRC 669 | 670 | #+BEGIN_NOTES 671 | Now the variables are properly placed into the states that use them. 672 | 673 | Chasing and Fighting states could inherit from an Aggroed state that holds a target. 674 | #+END_NOTES 675 | 676 | ** Example: Design Patterns 677 | The addition of sum types to C++ offers an alternative formulation for some 678 | design patterns. 679 | 680 | State machines and expressions are naturally modelled with sum types. 681 | 682 | #+BEGIN_NOTES 683 | Traditional runtime polymorphism approach can lead to bloated base class issue. 684 | 685 | Type erasure is another way to go. 686 | 687 | Sum types + visitor/pattern matching is a third possibility, particularly 688 | natural for things like ASTs. 689 | #+END_NOTES 690 | 691 | ** Example: Design Patterns 692 | - Command 693 | - Composite 694 | - State 695 | - Interpreter 696 | 697 | #+BEGIN_NOTES 698 | Command uses a flat, wide class hierarchy to encapsulate requests in objects. 699 | 700 | Composite: model part-whole hierarchies with uniform interface. 701 | 702 | State is obvious: simply replace the contained polymorphic object with a 703 | variant. 704 | 705 | Sum types are especially good for representing expressions (think JSON). 706 | 707 | Interpreter tackles the expression problem: easy to add new classes (use 708 | OO/interfaces) or new operations (use sum types/visitors)? 709 | #+END_NOTES 710 | 711 | ** Sum types vs Runtime Polymorphism :noexport: 712 | Runtime polymorphism (i.e. regular OO interface/implementation) allows manipulation of 713 | heterogeneous state with a uniform interface. 714 | 715 | Sum types allow manipulation of heterogenous state /and/ interface in a homogeneous way. 716 | 717 | #+BEGIN_NOTES 718 | This slide paraphrased from Andrei Alexandrescu's Dr Dobbs article, April 2002 (!) 719 | #+END_NOTES 720 | 721 | ** Designing with Types 722 | #+REVEAL_HTML:
723 | ~std::variant~ and ~std::optional~ are valuable tools that allow us to model the 724 | state of our business logic more accurately. 725 | 726 | When you match the types to the domain accurately, certain categories of tests 727 | just disappear. 728 | 729 | #+BEGIN_NOTES 730 | You don't have to test the edge cases where the representation can fall outside 731 | your reality - because that can't happen. 732 | #+END_NOTES 733 | 734 | ** Designing with Types 735 | #+REVEAL_HTML:
736 | Fitting types to their function more accurately makes code easier to understand 737 | and removes pitfalls. 738 | 739 | The bigger the codebase and the more vital the functionality, the more value 740 | there is in correct representation with types. 741 | 742 | #+BEGIN_NOTES 743 | When illegal states are unrepresentable, you don't have to worry about other 744 | programmers misunderstanding the code, or misusing data. In a sense, they 745 | /cannot/ write something that is wrong. 746 | 747 | And when I say "other programmers" of course I mean myself in 3 months... 748 | 749 | Questionably reusing fields, bending semantics, etc. These are bad practices. 750 | But they happen when we're chasing a deadline. 751 | #+END_NOTES 752 | 753 | ** Using Types to Constrain Behaviour 754 | #+REVEAL_HTML:
755 | We've seen how an expressive type system (with product and sum types) allows us 756 | to model state more accurately. 757 | 758 | "Phantom types" is one technique that helps us to model the /behaviour/ of our 759 | business logic in the type system. Illegal behaviour becomes a type error. 760 | 761 | ** Phantom Types: Before 762 | #+REVEAL_HTML:
763 | #+BEGIN_SRC cpp 764 | std::string GetFormData(); 765 | 766 | std::string SanitizeFormData(const std::string&); 767 | 768 | void ExecuteQuery(const std::string&); 769 | #+END_SRC 770 | An injection bug waiting to happen. 771 | 772 | #+BEGIN_NOTES 773 | Let's hope we don't meet little Bobby Tables, and that everywhere we execute a 774 | query we remembered to sanitize the data provided by the user. 775 | 776 | The type system is not helping us here. How can we use types to make sure that 777 | we stay safe? 778 | #+END_NOTES 779 | 780 | ** Phantom Types: The setup 781 | #+REVEAL_HTML:
782 | #+BEGIN_SRC cpp 783 | template 784 | struct FormData { 785 | explicit FormData(const string& input) : m_input(input) {} 786 | std::string m_input; 787 | }; 788 | 789 | struct sanitized {}; 790 | struct unsanitized {}; 791 | #+END_SRC 792 | ~T~ is the "Phantom Type" here. 793 | 794 | #+BEGIN_NOTES 795 | Note that the template argument is unused. It exists _only_ for compile time 796 | type checking. There is no runtime overhead. 797 | #+END_NOTES 798 | 799 | ** Phantom Types: After 800 | #+REVEAL_HTML:
801 | #+BEGIN_SRC cpp 802 | FormData GetFormData(); 803 | 804 | std::optional> 805 | SanitizeFormData(const FormData&); 806 | 807 | void ExecuteQuery(const FormData&); 808 | #+END_SRC 809 | 810 | #+BEGIN_NOTES 811 | User input is born unsanitized. 812 | 813 | It is impossible for us to execute unsanitized input. The compiler simply won't 814 | compile it. 815 | 816 | We've used types to help enforce the business logic. 817 | 818 | This is something similar to a strong typedef, or what enum class effectively 819 | does for integral types. This technique can also be used e.g. in a units 820 | library. 821 | #+END_NOTES 822 | 823 | ** Total Functions 824 | #+REVEAL_HTML:
825 | A /total function/ is a function that is defined for all inputs in its domain. 826 | 827 | #+ATTR_REVEAL: :frag appear 828 | ~template 829 | const T& min(const T& a, const T& b);~ 830 | 831 | #+ATTR_REVEAL: :frag appear 832 | ~float sqrt(float f);~ 833 | 834 | #+BEGIN_NOTES 835 | We are straying into the realm of Concepts here. 836 | 837 | I'm not saying that total is the same thing as "no preconditions". The type must 838 | satisfy the requirements on it. But you can see that with functions like ~sqrt~ 839 | there is a clear mismatch between the type of the function and the actual type 840 | of its domain. 841 | #+END_NOTES 842 | 843 | ** Let's play another game 844 | #+ATTR_REVEAL: :frag appear 845 | To help us see how total functions with the right types can result in 846 | unsurprising code. 847 | 848 | #+ATTR_REVEAL: :frag appear 849 | I'll give you a function signature with no names attached. 850 | 851 | #+ATTR_REVEAL: :frag appear 852 | You tell me what it's called... (and you'll even know how to implement it). 853 | 854 | #+ATTR_REVEAL: :frag appear 855 | The only rule... it must be a /total/ function. 856 | 857 | #+BEGIN_NOTES 858 | Assume regular types. Assume that the function is doing something "interesting" 859 | rather than "boring" when you have a choice. (ie. that it uses its argument). 860 | But you needn't assume anything else. 861 | 862 | And there are always ways to make things unexpected in C++. But assume nothing 863 | surprising here. 864 | #+END_NOTES 865 | 866 | ** Name That Function 867 | #+REVEAL_HTML:
868 | #+BEGIN_SRC cpp 869 | template 870 | T f(T); 871 | #+END_SRC 872 | 873 | #+ATTR_REVEAL: :frag appear 874 | ~identity~ 875 | 876 | #+ATTR_REVEAL: :frag appear 877 | #+BEGIN_SRC cpp 878 | int f(int); 879 | #+END_SRC 880 | 881 | #+BEGIN_NOTES 882 | Note the odd situation here: we know more about ~f(T)~ than we do about 883 | ~f(int)~. 884 | #+END_NOTES 885 | 886 | ** Name That Function 887 | #+REVEAL_HTML:
888 | #+BEGIN_SRC cpp 889 | template 890 | T f(pair); 891 | #+END_SRC 892 | 893 | #+ATTR_REVEAL: :frag appear 894 | ~first~ 895 | 896 | ** Name That Function 897 | #+REVEAL_HTML:
898 | #+BEGIN_SRC cpp 899 | template 900 | T f(bool, T, T); 901 | #+END_SRC 902 | 903 | #+ATTR_REVEAL: :frag appear 904 | ~select~ 905 | 906 | ** Name That Function 907 | #+REVEAL_HTML:
908 | #+BEGIN_SRC cpp 909 | template 910 | U f(function, T); 911 | #+END_SRC 912 | 913 | #+ATTR_REVEAL: :frag appear 914 | ~apply~ or ~call~ 915 | 916 | ** Name That Function 917 | #+REVEAL_HTML:
918 | #+BEGIN_SRC cpp 919 | template 920 | vector f(vector); 921 | #+END_SRC 922 | 923 | #+ATTR_REVEAL: :frag appear 924 | ~reverse~, ~shuffle~, ... 925 | 926 | #+BEGIN_NOTES 927 | For simplicity, I haven't written this signature in terms of iterators, but it 928 | would be just the same. 929 | #+END_NOTES 930 | 931 | ** Name That Function 932 | #+REVEAL_HTML:
933 | #+BEGIN_SRC cpp 934 | template 935 | T f(vector); 936 | #+END_SRC 937 | 938 | #+ATTR_REVEAL: :frag appear 939 | Not possible! It's a partial function - the ~vector~ might be empty. 940 | 941 | #+ATTR_REVEAL: :frag appear 942 | #+BEGIN_SRC cpp 943 | T& vector::front(); 944 | #+END_SRC 945 | 946 | ** Name That Function 947 | #+REVEAL_HTML:
948 | #+BEGIN_SRC cpp 949 | template 950 | optional f(vector); 951 | #+END_SRC 952 | 953 | ** Name That Function 954 | #+REVEAL_HTML:
955 | #+BEGIN_SRC cpp 956 | template 957 | vector f(function, vector); 958 | #+END_SRC 959 | 960 | #+ATTR_REVEAL: :frag appear 961 | ~transform~ 962 | 963 | ** Name That Function 964 | #+REVEAL_HTML:
965 | #+BEGIN_SRC cpp 966 | template 967 | vector f(function, vector); 968 | #+END_SRC 969 | 970 | #+ATTR_REVEAL: :frag appear 971 | ~remove_if~, ~partition~, ... 972 | 973 | ** Name That Function 974 | #+REVEAL_HTML:
975 | #+BEGIN_SRC cpp 976 | template 977 | T f(optional); 978 | #+END_SRC 979 | 980 | #+ATTR_REVEAL: :frag appear 981 | Not possible! 982 | 983 | ** Name That Function 984 | #+REVEAL_HTML:
985 | #+BEGIN_SRC cpp 986 | template 987 | V f(map, K); 988 | #+END_SRC 989 | 990 | #+ATTR_REVEAL: :frag appear 991 | Not possible! (The key might not be in the ~map~.) 992 | 993 | #+ATTR_REVEAL: :frag appear 994 | #+BEGIN_SRC cpp 995 | V& map::operator[](const K&); 996 | #+END_SRC 997 | 998 | ** Name That Function 999 | #+REVEAL_HTML:
1000 | #+BEGIN_SRC cpp 1001 | template 1002 | optional f(map, K); 1003 | #+END_SRC 1004 | 1005 | #+ATTR_REVEAL: :frag appear 1006 | ~lookup~ 1007 | 1008 | ** What Just Happened? 1009 | I gave you /almost nothing/. 1010 | 1011 | No variable names. No function names. No type names. 1012 | 1013 | Just bare type signatures. 1014 | 1015 | #+ATTR_REVEAL: :frag appear 1016 | You were able to tell me exactly what the functions should be called, and likely 1017 | knew instantly how to implement them. 1018 | 1019 | #+ATTR_REVEAL: :frag appear 1020 | You will note that partial functions gave us some issues... 1021 | 1022 | #+BEGIN_NOTES 1023 | Naming is one of the hardest problems in Comp Sci. Getting the types right is 1024 | much easier. And if your types model the logic properly, perhaps you have 1025 | "self-documenting code"? 1026 | #+END_NOTES 1027 | 1028 | ** Well-typed Functions 1029 | #+REVEAL_HTML:
1030 | Writing /total functions/ with well-typed signatures can tell us a lot about 1031 | functionality. 1032 | 1033 | Using types appropriately makes interfaces unsurprising, safer to use and harder 1034 | to misuse. 1035 | 1036 | Total functions make more test categories vanish. 1037 | 1038 | ** About Testing... 1039 | In a previous talk, I talked about unit testing and in particular property-based testing. 1040 | 1041 | #+ATTR_REVEAL: :frag appear 1042 | Effectively using types can reduce test code. 1043 | 1044 | #+ATTR_REVEAL: :frag appear 1045 | Property-based tests say "for all values, this property is true". 1046 | 1047 | #+ATTR_REVEAL: :frag appear 1048 | That is exactly what types /are/: universal quantifications about what can be 1049 | done with data. 1050 | 1051 | #+ATTR_REVEAL: :frag appear 1052 | Types scale better than tests. Instead of TDD, maybe try TDD! 1053 | 1054 | #+BEGIN_NOTES 1055 | C++'s type system isn't yet powerful enough to be able to say goodbye to tests, 1056 | but it is powerful enough that used effectively, we can reduce some of the 1057 | drudgery of writing tests. 1058 | 1059 | Any time you're thinking something is true for all values, that's what a type 1060 | can do. 1061 | #+END_NOTES 1062 | 1063 | ** Further Down the Rabbit Hole 1064 | #+REVEAL_HTML:
1065 | - http://en.wikipedia.org/wiki/Algebraic_data_type 1066 | - http://chris-taylor.github.io/blog/2013/02/10/the-algebra-of-algebraic-data-types/ 1067 | - https://vimeo.com/14313378 (Effective ML: Making Illegal States Unrepresentable) 1068 | - http://www.infoq.com/presentations/Types-Tests (Types vs Tests: Strange Loop 2012) 1069 | 1070 | ** Thanks For Listening 1071 | #+REVEAL_HTML:
1072 | "On the whole, I'm inclined to say that when in doubt, make a new type." 1073 | #+REVEAL_HTML:
1074 | -- Martin Fowler, [[http://martinfowler.com/ieeeSoftware/whenType.pdf][/When to Make a Type/]] 1075 | #+REVEAL_HTML:

1076 | "Don't set a flag; set the data." 1077 | #+REVEAL_HTML:
1078 | -- Leo Brodie, /[[http://thinking-forth.sourceforge.net/][Thinking Forth]]/ 1079 | #+REVEAL_HTML:
1080 | 1081 | ** Goals for Well-typed Code 1082 | - Make illegal states unrepresentable 1083 | - Use ~std::variant~ and ~std::optional~ for formulations that 1084 | - are more natural 1085 | - fit the business logic state better 1086 | - Use phantom types for safety 1087 | - Make illegal behaviour a compile error 1088 | - Write total functions 1089 | - Unsurprising behaviour 1090 | - Easy to use, hard to misuse 1091 | 1092 | ** Epilogue 1093 | 1094 | A taste of algebra with datatypes 1095 | 1096 | ** A Taste of Algebra with Datatypes 1097 | How many values? 1098 | #+BEGIN_SRC cpp 1099 | template 1100 | class vector; 1101 | #+END_SRC 1102 | 1103 | #+ATTR_REVEAL: :frag appear 1104 | We can define a ~vector~ recursively: 1105 | 1106 | #+ATTR_REVEAL: :frag appear 1107 | ${v(t)} = {1 + t v(t)}$ 1108 | 1109 | #+ATTR_REVEAL: :frag appear 1110 | (empty vector or (+) head element and (*) tail vector) 1111 | 1112 | ** A Taste of Algebra with Datatypes 1113 | And rearrange... 1114 | 1115 | ${v(t)} = {1 + t v(t)}$ 1116 | #+ATTR_REVEAL: :frag appear 1117 | ${v(t) - t v(t)} = {1}$ 1118 | #+ATTR_REVEAL: :frag appear 1119 | ${v(t) (1-t)} = {1}$ 1120 | #+ATTR_REVEAL: :frag appear 1121 | ${v(t)} = {{1} \over {1-t}}$ 1122 | 1123 | #+ATTR_REVEAL: :frag appear 1124 | What does that mean? Subtracting and dividing types? 1125 | 1126 | ** A Taste of Algebra with Datatypes 1127 | When we don't know how to interpret something mathematical? 1128 | 1129 | ${v(t)} = {{1} \over {1-t}}$ 1130 | 1131 | #+REVEAL_HTML:

Let's ask Wolfram Alpha.

1132 | 1133 | ** A Taste of Algebra with Datatypes 1134 | Series expansion at ${t = 0}$: 1135 | 1136 | ${1 + t + t^2 + t^3 + t^4 +{ }...}$ 1137 | 1138 | #+ATTR_REVEAL: :frag appear 1139 | A ~vector~ can have: 1140 | #+ATTR_REVEAL: :frag (appear) 1141 | - 0 elements (${1}$) 1142 | - or (+) 1 element (${t}$) 1143 | - or (+) 2 elements (${t^2}$) 1144 | - etc. 1145 | 1146 | ** Goals for Well-typed Code 1147 | - Make illegal states unrepresentable 1148 | - Use ~std::variant~ and ~std::optional~ for formulations that 1149 | - are more natural 1150 | - fit the business logic state better 1151 | - Use phantom types for safety 1152 | - Make illegal behaviour a compile error 1153 | - Write total functions 1154 | - Unsurprising behaviour 1155 | - Easy to use, hard to misuse 1156 | -------------------------------------------------------------------------------- /submission.org: -------------------------------------------------------------------------------- 1 | Career (20+ years) games industry programmer; generic and functional programming 2 | enthusiast. So I'm keen on performance (of course, I use C++) and I like using 3 | theory to gaining insights into practical problems. Currently a principal 4 | engineer at Blizzard Entertainment where I drive a lot of internal C++ learning. 5 | I study everything I can because the world is interesting. I've previously 6 | (2015) presented at C++Now and CppCon. 7 | 8 | -- 9 | 10 | Using Types Effectively 11 | 12 | C++ has a pretty good type system, and modern C++ gives us a greater ability 13 | than ever before to use that type system for good: to make APIs easier to use 14 | and harder to misuse, to make our datatypes more closely express our intent, and 15 | generally to make code safer, more obvious in function and perhaps even faster. 16 | 17 | This is an interactive session - incorporating games played between presenter 18 | and audience, even - taking a look at choices available to us as datatype and 19 | API designers, and examining how a little knowledge about the algebra of 20 | algebraic datatypes can help. We'll see why std::optional and (hopefully soon) 21 | std::variant will quickly become an essential part of everyone's toolbox, and 22 | also explore how types can be used to express not just the structure of data, 23 | but also the behaviour of objects and functions. 24 | 25 | -- 26 | 27 | * Introduction 28 | ** What is a type? 29 | ** Why do we have types? 30 | 31 | * Game 1: Types as sets of values 32 | ** Integral types 33 | ** Equivalence by equal cardinality 34 | 35 | * Game 2: Product types 36 | ** std::pair 37 | ** structs 38 | ** std::tuple 39 | ** reasoning about generic product types 40 | 41 | * Game 3: Sum types 42 | ** std::optional 43 | ** std::variant 44 | ** reasoning about generic sum types 45 | 46 | * Algebraic datatypes 47 | ** Equivalences 48 | ** Choices of formulation 49 | ** An aside: how to interpret std::vector 50 | 51 | * Making illegal states unrepresentable 52 | ** Example: a network connection in various states 53 | ** Example: a nullable field, asynchronously fetched 54 | ** Example: monster AI 55 | 56 | * Designing with type algebra 57 | ** A look at some design patterns 58 | ** More naturally modelling design patterns 59 | ** Sum types vs runtime polymorphism 60 | ** Designing with std::optional and std::variant 61 | 62 | * Constraining behaviour with types 63 | ** Phantom types 64 | ** Example: using phantom types to prevent injection attacks 65 | 66 | * Game 4: Name that function 67 | ** How appropriate typing makes functionality obvious 68 | ** Total vs partial functions 69 | ** Pitfalls of partial functions 70 | 71 | * TDD (Type-driven development) 72 | ** Make tests disappear 73 | ** Further down the rabbit hole 74 | ** Goals for well-typed code 75 | -------------------------------------------------------------------------------- /variant-tweet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbeno/using-types-effectively/1b5c61f7013d228e1207cf05c61e51db50f042eb/variant-tweet.png --------------------------------------------------------------------------------