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

The Curiously Recurring Pattern of Coupled Types

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.

This talk was given together with Adi Shavit from CoreC++, Israel.

Björn Fahller

October 25, 2018
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. @bjorn_fahller :: @adishavit
    The Curiously Recurring
    Coupled Types Pattern
    Adi Shavit and Björn Fahller
    @adishavit :: @bjorn_fahller

    View Slide

  2. @bjorn_fahller :: @adishavit
    Curious API Pattern Examples

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. @bjorn_fahller :: @adishavit
    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

    View Slide

  11. @bjorn_fahller :: @adishavit
    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

    View Slide

  12. @bjorn_fahller :: @adishavit
    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

    View Slide

  13. @bjorn_fahller :: @adishavit
    p1
    Position p1{...};
    2D Space

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. @bjorn_fahller :: @adishavit
    Do we have time?

    View Slide

  22. @bjorn_fahller :: @adishavit

    View Slide

  23. @bjorn_fahller :: @adishavit

    View Slide

  24. @bjorn_fahller :: @adishavit

    ?

    View Slide

  25. @bjorn_fahller :: @adishavit

    View Slide

  26. @bjorn_fahller :: @adishavit

    timepoint timepoint duration

    View Slide

  27. @bjorn_fahller :: @adishavit

    timepoint timepoint
    ?

    View Slide

  28. @bjorn_fahller :: @adishavit

    timepoint
    timepoint
    duration

    View Slide

  29. @bjorn_fahller :: @adishavit
    Pointer Arithmetic

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. @bjorn_fahller :: @adishavit
    Are we seeing it yet?

    View Slide

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

    View Slide

  40. @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.

    View Slide

  41. @bjorn_fahller :: @adishavit
    The Affine Space: Definitions
    affine space points vectors scalars
    vector space
    scalar
    affine space
    v p q
    ■ p-q=v
    ■ p+v=q

    View Slide

  42. @bjorn_fahller :: @adishavit
    The Affine Space Type API
    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

    View Slide

  43. @bjorn_fahller :: @adishavit

    View Slide

  44. @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; // ???

    View Slide

  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; // ERROR!

    View Slide

  46. @bjorn_fahller :: @adishavit
    Iterators

    View Slide

  47. @bjorn_fahller :: @adishavit
    Iterators
    using T = float;
    std::vector 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 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);

    View Slide

  48. @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...

    View Slide

  49. @bjorn_fahller :: @adishavit
    Remember the Mars Climate Orbiter
    pound-force seconds
    newton seconds,

    View Slide

  50. @bjorn_fahller :: @adishavit
    An Imaginary GUI library

    View Slide

  51. @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};

    View Slide

  52. @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);

    View Slide

  53. @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);

    View Slide

  54. @bjorn_fahller :: @adishavit
    Demo
    github.com/rollbear/affine_types
    gcc.godbolt.org/z/IJLKFG

    View Slide

  55. @bjorn_fahller :: @adishavit
    Affine Space Concepts
    template
    concept Affine_space = Regular and
    Vector_space 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

    View Slide

  56. @bjorn_fahller :: @adishavit
    The Affine Combination
    affine combination
    1 ⇒ point
    0 ⇒ vector
    undefined
    barycentric coordinates

    View Slide

  57. @bjorn_fahller :: @adishavit
    The Affine Combination
    m=(a+b)/2 // midpoint
    int a,b m=(a+b)/2
    m
    int* a; int* b; m=(a+b)/2
    m=a+(b-a)/2
    ½*a+½*b ⇒ ← Affine Combination!
    p q r
    (p+q+r)/3 CoG = ⅓*p + ⅓*q + ⅓*r ⇒ ⅓ ⅓ ⅓

    View Slide

  58. @bjorn_fahller :: @adishavit
    Summary
    ● about the semantics to help build the types
    ● strong APIs:

    ○ ⇒ A semantically incorrect expression becomes a compilation error
    ● Affine-space type-relations are very common
    ● them and math delivers a powerful and consistent API
    ● : Learn more about the math of types: Abstract Algebra, Category Theory
    When in doubt, do what does!

    View Slide

  59. @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)
    ● Compiler Explorer “GUI” code example
    ○ gcc.godbolt.org/z/IJLKFG

    View Slide

  60. @bjorn_fahller :: @adishavit
    @adishavit @bjorn_fahller
    @rollbear
    @longbeard
    The Curiously Recurring Pattern of Coupled Types

    View Slide

  61. @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

    View Slide