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

Higher order functions for the rest of us

Higher order functions for the rest of us

Higher order functions, i.e., functions that accept functions as arguments, or return functions, are a core part of functional programming. The C++ standard library also has an abundance of higher order functions, but it is rare for C++ developers to write their own.

I will show some simple types of higher order functions you can easily write yourself, that will reduce repetition, increase code clarity, and make you a more productive programmer.

Björn Fahller

April 12, 2018
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    1/41 compose([](auto const& s) { return s == "foo";}, std::mem_fn(&foo::name)) Higher order functions for the rest of us Björn Fahller
  2. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    2/41 Definition A higher-order function is a function that takes other functions as arguments or returns a function as result.
  3. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    3/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... }
  4. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    4/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... } template <typename Iterator, typename Predicate> bool none_of(Iterator i, Iterator e, Predicate predicate) { while (i != e) { if (predicate(*i)) return false; i++; } return true; }
  5. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    5/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... } ... int num; ... while (std::any_of(std::begin(v), std::end(v), [num](int x){ return x == num; })) { ... }
  6. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    7/41 auto equals(int num) { return [num](int x){ return x == num; }; }
  7. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    8/41 template <typename T> auto equals(T num) { return [num](auto const& x){ return x == num; }; }
  8. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    9/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), equals(0)) { ... } ... int num; ... while (std::any_of(std::begin(v), std::end(v), equals(num)) { ... }
  9. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    10/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  10. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    11/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  11. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    12/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  12. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    13/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  13. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    14/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip actual) { return (desired & mask) == (actual & mask); }; }
  14. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    15/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip actual) { return (desired & mask) == (actual & mask); }; } std::vector<ip> v; ... auto i = std::remove_if(v.begin(), v.end(), ip_matches({192,168,1,1}, {255,255,0,0}));
  15. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    16/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() const { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; };
  16. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    17/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() const { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; };
  17. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    18/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() const { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; To match, for example the address of an ipif, we need to make the ip_matches() predicate work on a member.
  18. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    19/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x))
  19. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    20/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; Given: f1(y) -> z ip_matches(ip) -> bool and f2(x) -> y select_addr(ipif) -> ip We want a composition f(x)->z as f1(f2(x))
  20. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    21/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; template <typename F1, typename F2> auto compose(F1 f1, F2 f2) { return [=](const auto& x) { return f1(f2(x)); }; } Given: f1(y) -> z ip_matches(ip) -> bool and f2(x) -> y select_addr(ipif) -> ip We want a composition f(x)->z as f1(f2(x))
  21. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    22/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr));
  22. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    23/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; ip select_addr(ipif const& interface) { return interface.addr(); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr));
  23. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    24/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; auto addr_matches(ip addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr);
  24. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    25/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; auto addr_matches(ip addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), addr_matches({192,168,1,1}));
  25. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    26/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } ... }; inline ip select_gw(ipif const& interface) { return interface.gw(); } inline ipif::state_type select_state(ipif const& interface) { return interface.state(); }
  26. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    27/41 auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  27. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    28/41 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](auto const& x) { return (ps(x) && ...); }; } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  28. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    29/41 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](auto const& x) { return (ps(x) && ...); }; } auto addr_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(addr, mask), select_addr); } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  29. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    30/41 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](auto const& x) { return (ps(x) && ...); }; } auto addr_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(addr, mask), select_addr); } auto state_is(ipif::state_type state) { return compose(equals(state), select_state); } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  30. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    31/41 for_each(v.begin(), v.end(), if_then(when_all(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif::off)), set_state(ipif::on)));
  31. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    32/41 template <typename Predicate, typename Action> auto if_then(Predicate predicate, Action action) { return [=](auto&& x) { if (predicate(x)) { action(std::forward<decltype(x)>(x)); } }; } for_each(v.begin(), v.end(), if_then(when_all(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif::off)), set_state(ipif::on)));
  32. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    33/41 template <typename Predicate, typename Action> auto if_then(Predicate predicate, Action action) { return [=](auto&& x) { if (predicate(x)) { action(std::forward<decltype(x)>(x)); } }; } auto set_state(ipif::state_type state) { return [=](ipif& interface) { interface.set_state(state); }; } for_each(v.begin(), v.end(), if_then(when_all(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif::off)), set_state(ipif::on)));
  33. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    35/41 Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) Domain specific functions • ip_matches(ip, mask) • select_addr(ipif) • select_gw(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • addr_matches(ip) • state_is(state_type)
  34. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    36/41 https://github.com/rollbear/lift Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) Domain specific functions • ip_matches(ip, mask) • select_addr(ipif) • select_gw(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • addr_matches(ip) • state_is(state_type)
  35. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    37/41 Take away messages • Write functions that uses auto return type to create lambdas
  36. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    38/41 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state
  37. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    39/41 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions
  38. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    40/41 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions • And give names to the compositions
  39. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    41/41 Björn Fahller https://github.com/rollbear/lift [email protected] @bjorn_fahller @rollbear cpplang, swedencpp Higher order functions for the rest of us