Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NDC Oslo - The Curiously Recurring Pattern of Coupled Types

NDC Oslo - The Curiously Recurring Pattern of Coupled Types

Why can pointers be subtracted but not added?

What do raw C pointers, STL iterators, std::chrono types, and 2D/3D geometric primitives have in common?

In this talk we will present some curiously coupled data types that frequently occur in your programs, together forming notions that you are already intuitively familiar with. We will shine a light on the mathematical notion of Affine Spaces, and how they guide stronger design. We will review the properties of affine spaces and show how they improve program semantics, stronger type safety and compile time enforcement of these semantics.

By showing motivational examples, we will introduce you to the mathematical notion of affine spaces. The main focus will then be on how affine space types and their well defined semantics shape expressive APIs.

We will give examples and guidelines for creating your own affine types. Although the examples in the talk will use C++, the general concepts are applicable to other strongly typed programming languages.

5d138ccf47c8d79aa8f0d29900f9e07b?s=128

Björn Fahller

June 20, 2019
Tweet

Transcript

  1. @bjorn_fahller :: @adishavit Curiousl Recurrin Patter of Couple T e

    Ad Shavi an Björ Fahller @adishavit :: @bjorn_fahller
  2. @bjorn_fahller :: @adishavit Curious API Pattern Examples 2

  3. @bjorn_fahller :: @adishavit 3 v1 Vec2D v1{...}; 2D Space

  4. @bjorn_fahller :: @adishavit 4 v1 v2 Vec2D v1{...}; Vec2D v2{...};

    2D Space
  5. @bjorn_fahller :: @adishavit 5 v1 v2 Vec2D v1{...}; Vec2D v2{...};

    2D Space
  6. @bjorn_fahller :: @adishavit 6 v1 v2 v3 Vec2D v1{...}; Vec2D

    v2{...}; auto v3 = v1+v2; 2D Space
  7. @bjorn_fahller :: @adishavit 7 v1 v2 v1 + v2 -v2

    Vec2D v1{...}; Vec2D v2{...}; auto v3 = v1+v2; -v2; 2D Space
  8. @bjorn_fahller :: @adishavit 8 v1 v2 v1 + v2 -v2

    Vec2D v1{...}; Vec2D v2{...}; auto v3 = v1+v2; -v2; 2D Space
  9. @bjorn_fahller :: @adishavit 9 v1 v2 v1 + v2 -v2

    v1 - v2 Vec2D v1{...}; Vec2D v2{...}; auto v3 = v1+v2; auto v4 = v1-v2; 2D Space
  10. @bjorn_fahller :: @adishavit 10 v1 v2 v1 + v2 -v2

    v1 - v2 2 * v2 Vec2D v1{...}; Vec2D v2{...}; auto v3 = v1+v2; auto v4 = v1-v2; auto v5 = 2*v2; 2D Space
  11. @bjorn_fahller :: @adishavit 11 v1 v2 v1 + v2 -v2

    v1 - v2 2 * v2 Vec2D v1{...}; Vec2D v2{...}; auto v3 = v1+v2; auto v4 = v1-v2; auto v5 = 2*v2; v1 * v2 2D Space
  12. @bjorn_fahller :: @adishavit 12 v1 v2 v1 + v2 -v2

    v1 - v2 2 * v2 Vec2D v1{...}; Vec2D v2{...}; auto v3 = v1+v2; auto v4 = v1-v2; auto v5 = 2*v2; v1 * v2 2D Space
  13. @bjorn_fahller :: @adishavit 13 p1 Position p1{...}; 2D Space

  14. @bjorn_fahller :: @adishavit Position p1{...}; Position p2{...}; 14 p1 p2

    2D Space
  15. @bjorn_fahller :: @adishavit Position p1{...}; Position p2{...}; p2 - p1;

    15 p1 p2 p2 - p1 2D Space
  16. @bjorn_fahller :: @adishavit Position p1{...}; Position p2{...}; Vec2D v =

    p2 - p1; 16 p1 p2 p2 - p1 2D Space
  17. @bjorn_fahller :: @adishavit Position p1{...}; Position p2{...}; Vec2D v =

    p2 - p1; Vec2D v2{...}; v2 17 p1 p2 p2 - p1 2D Space
  18. @bjorn_fahller :: @adishavit Position p1{...}; Position p2{...}; Vec2D v =

    p2 - p1; Vec2D v2{...}; auto p3 = p1 + v2; p3 v2 18 p1 p2 p2 - p1 2D Space
  19. @bjorn_fahller :: @adishavit Position p1{...}; Position p2{...}; Vec2D v =

    p2 - p1; Vec2D v2{...}; auto p3 = p1 + v2; p3 + p2 p3 v2 19 p1 p2 p2 - p1 2D Space
  20. @bjorn_fahller :: @adishavit Position p1{...}; Position p2{...}; Vec2D v =

    p2 - p1; Vec2D v2{...}; auto p3 = p1 + v2; p3 + p2 p3 v2 20 p1 p2 p2 - p1 2D Space
  21. @bjorn_fahller :: @adishavit Do we have time? 21

  22. @bjorn_fahller :: @adishavit <chrono> 22

  23. @bjorn_fahller :: @adishavit <chrono> 23

  24. @bjorn_fahller :: @adishavit <chrono> ? 24

  25. @bjorn_fahller :: @adishavit <chrono> 25

  26. @bjorn_fahller :: @adishavit <chrono> timepoint timepoint duration 26

  27. @bjorn_fahller :: @adishavit <chrono> timepoint timepoint ? 27

  28. @bjorn_fahller :: @adishavit <chrono> timepoint timepoint duration 28

  29. @bjorn_fahller :: @adishavit Pointer Arithmetic 29

  30. @bjorn_fahller :: @adishavit Let’s dive into pointer arithmetics! A array[]

    = 30
  31. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = 31
  32. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = A* p2 = 32
  33. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = A* p2 = auto d = p2 - p1 33
  34. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = A* p2 = auto d = p2 - p1 34
  35. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = A* p2 = auto d = p2 - p1 auto s = p2 + p1 35
  36. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = A* p2 = auto d = p2 - p1 auto s = p2 + p1 36
  37. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = A* p2 = auto d = p2 - p1 auto x = p2 - d/2 37
  38. @bjorn_fahller :: @adishavit Pointer arithmetics! A array[] = A* p1

    = A* p2 = auto d = p2 - p1 auto x = p2 - d/2 38
  39. @bjorn_fahller :: @adishavit Are we seeing it yet? 39

  40. @bjorn_fahller :: @adishavit 40 Meanwhile on Twitter...

  41. @bjorn_fahller :: @adishavit The Affine Space Intuitively: 1. A point:

    a position specified with coordinate values ◦ a.k.a. location, address... 2. A vector: the difference between two points ◦ a.k.a. shift, offset, displacement, duration... If an origin is specified, then a point can be represented by a vector from the origin, however, a point is still not a vector in coordinate-free concepts. 41
  42. @bjorn_fahller :: @adishavit The Affine Space: Definitions 1. An affine

    space has two types of entities (i.e. types): points and vectors (and scalars too) 2. The vectors form a normal vector space: 1. Closure under the usual vector operations: Addition, Subtraction, Multiplication by scalar, Linear combinations... 3. An affine space has the following properties: 1. There is a unique vector v related to a pair of points p and q, with: ▪ p-q=v : Point subtraction returns a Vector ▪ p+v=q : Vector & Point addition returns a Point. Most familiar types are closed over operations (Monoids). Affine-space types are more general. 42
  43. @bjorn_fahller :: @adishavit The Affine Space Type API Given Point,

    Vector, Scalar: Vector operations Vector + Vector -> Vector scalar * Vector -> Vector Vector * Scalar -> Vector Vector - Vector -> Vector -Vector -> Vector Affine operations Point - Point -> Vector Point + Vector -> Point Point - Vector -> Point 43
  44. @bjorn_fahller :: @adishavit <chrono> 44

  45. @bjorn_fahller :: @adishavit using namespace std::chrono; auto beg = system_clock::now();

    // start time // ... auto end = system_clock::now(); // end time auto dur = end - beg; // total duration auto almost = beg + dur - 1ms; // just before end auto next = beg + 2*dur; // twice as long auto ago = beg - end; // a negative time offset auto utcnow = utc_clock::now(); // UTC time (C++20) auto utcnxt = utcnow + 2*dur; // OK: when auto meh = utcnow - beg; // ??? <chrono> 45
  46. @bjorn_fahller :: @adishavit using namespace std::chrono; auto beg = system_clock::now();

    // start time // ... auto end = system_clock::now(); // end time auto dur = end - beg; // total duration auto almost = beg + dur - 1ms; // just before end auto next = beg + 2*dur; // twice as long auto ago = beg - end; // a negative time offset auto utcnow = utc_clock::now(); // UTC time (C++20) auto utcnxt = utcnow + 2*dur; // OK: when auto meh = utcnow - beg; // ERROR! <chrono> 46
  47. @bjorn_fahller :: @adishavit Iterators 47

  48. @bjorn_fahller :: @adishavit Iterators using T = float; std::vector<T> vec

    = {0,1,2,3}; auto beg = vec.begin(); auto end = vec.end(); int cnt = end - beg; auto last = beg + cnt - 1; int neg = beg - end; using T = float; std::set<T> set={0,1,2,3}; auto beg = set.begin(); auto end = set.end(); auto cnt = std::distance(beg, end); auto last = std::next(beg, cnt - 1); auto neg = std::distance(end, beg); 48
  49. @bjorn_fahller :: @adishavit Counter Examples using namespace cv; Point pos

    = { 0,0 }; Point next_pos = pos + Point{Vec2i{ 42,0 }}; // WAT? Point dir = next_pos - pos; // Umm.. Point huh = -pos; // Huh? Point hmm = pos * 2; // Hmmm... 49
  50. @bjorn_fahller :: @adishavit Remember the Mars Climate Orbiter 50 "Software

    that calculated the total impulse produced by thruster firings produced results in pound-force seconds. The trajectory calculation software then used these results, expected to be in newton seconds, to update the predicted position of the spacecraft."
  51. @bjorn_fahller :: @adishavit An Imaginary GUI library 51

  52. @bjorn_fahller :: @adishavit An Imaginary GUI library using Size =

    Vec2D; class Window { public: Window(Point bl, Size size); ... ... }; Point p1{...}; Point p2{...}; Window w{p1, p2-p1}; 52
  53. @bjorn_fahller :: @adishavit An Imaginary GUI library using Size =

    Vec2D; class Window { public: Window(Point bl, Size size); void translate(Vec2D offset); ... }; Point p1{...}; Point p2{...}; Window w{p1, p2-p1}; w.translate(p2-p1); 53
  54. @bjorn_fahller :: @adishavit An Imaginary GUI library using Size =

    Vec2D; class Window { public: Window(Point bl, Size size); void translate(Vec2D offset); void move_to(Point dst); }; Point p1{...}; Point p2{...}; Window w{p1, p2-p1}; w.translate(p2-p1); w.move_to(p1); 54
  55. @bjorn_fahller :: @adishavit Demo 55 github.com/rollbear/affine_types gcc.godbolt.org/z/IJLKFG

  56. @bjorn_fahller :: @adishavit Affine Space Concepts 56 template <typename P,

    typename V, typename S> concept Affine_space = Regular<P> and Vector_space<V, S> and requires (P const& p, V const& v) { { p - p } -> V; { p + v } -> P; { p - v } -> P; } and requires (P& p, V const& v) { { p += v } -> P&; { p -= v } -> P&; }; } https://github.com/petter-holmberg/elements
  57. @bjorn_fahller :: @adishavit The Affine Combination 1. An affine combination

    is a weighted sum of one of more points such that: 1. When the total sum of the weights is exactly 1 ⇒ the resulting type is a point 2. When the total sum of the weights is exactly 0 ⇒ the resulting type is a vector 3. Otherwise it is undefined The affine combination weight-tuples are a.k.a. the barycentric coordinates of a point. 57
  58. @bjorn_fahller :: @adishavit The Affine Combination m=(a+b)/2 // midpoint When

    int a,b , what is m=(a+b)/2 ? m is the mid-point (up-to truncation and overflow) When int* a; int* b; (pointers into an array), what is m=(a+b)/2 ? Argh! Cannot add pointers!!! Rewrite as: m=a+(b-a)/2 Or: ½*a+½*b ⇒ ½ + ½ = 1 ← Affine Combination! Given 2D points: p,q,r, the center-of-gravity is the affine combination: (p+q+r)/3 or CoG = ⅓*p + ⅓*q + ⅓*r ⇒ ⅓ + ⅓ + ⅓ = 1 58
  59. @bjorn_fahller :: @adishavit Summary • Think about the semantics to

    help build the types • Create strong APIs: Valid Syntax ⇔ Valid Semantics ◦ ⇒ A semantically incorrect expression becomes a compilation error • Affine-space type-relations are very common • Identify them and math delivers a powerful and consistent API • Generalise: Learn more about the math of types: Abstract Algebra, Category Theory When in doubt, do what <chrono> does! 59
  60. @bjorn_fahller :: @adishavit Resources • Affine Space Types: Adi Shavit,

    blog post (+many links) ◦ videocortex.io/2018/Affine-Space-Types/ • Type safe C++ – LOL! :-), Björn Fahller, ACCU 2018 ◦ youtu.be/SWHvNvY-PHw • Operator Overloading: History, Principles and Practice, Ben Deane, CppCon 2018 ◦ youtu.be/zh4EgO13Etg?t=831 (@13:53) • How I learned to stop worrying and love the C++ type system, Peter Sommerlad, CppNow 2019 ◦ youtu.be/U0DyF4J4beo 60
  61. @bjorn_fahller :: @adishavit @adishavit @bjorn_fahller @rollbear @longbeard The Curiously Recurring

    Pattern of Coupled Types 61
  62. @bjorn_fahller :: @adishavit Why can pointers be subtracted but not

    added? What do raw C pointers, STL iterators, std::chrono types, and 2D/3D geometric primitives have in common? In this talk we will present some curiously coupled data types that frequently occur in your programs, together forming notions that you are already intuitively familiar with. We will shine a light on the mathematical notion of Affine Spaces, and how they guide stronger design. We will review the properties of affine spaces and show how they improve program semantics, stronger type safety and compile time enforcement of these semantics. By showing motivational examples, we will introduce you to the mathematical notion of affine spaces. The main focus will then be on how affine space types and their well defined semantics shape expressive APIs. We will give examples and guidelines for creating your own affine types. * Show familiar examples of situations of affine space semantics - pointer arithmetic - iterators - chrono - coordinate systems * Mathematical definitions/properties * Describe properties of affine space types - operators and relations - [show a Concept for affine space types, tbd] * Show how to write a simple affine space strong type template. * Parallels to unit systems 62