900 likes | 2.15k Views
Presented at ACCU Oxford (14th September 2015) <br><br>Functional C ? As opposed to what — dysfunctional? Well, kind of, yeah. Sure, in C the principal unit of composition is called a function, but that doesn't mean it's a functional language. And the idea of restricting mutability of state gets a nod with const, but it's a nod not a hug. And the STL shows influences of functional programming, although it falls short of being compositional. And, yes, sure, C 11 has lambdas, but then again, these days, who doesn't? Lambda calculus was invented in the 1930s. <br><br>This talk looks at how to express functional programming ideas in (post)modern C in a way that can be considered idiomatic to C , rather than trying to use the power of overloading and meta-programming to pretend C is Haskell or Lisp. In short, immutability beyond const and into shared and persistent data structures, concurrency beyond threading and locks, and thinking about functions as transformations and units of composition rather than actions.
E N D
Functional C++ @KevlinHenney
recursion mathematics idempotence pattern matching unification monads higher-order functions functional programming first-class functions lambdas pure functions declarative immutability currying non-strict evaluation lists statelessness
class heating_system { public: void turn_on(); void turn_off(); ... };
class timer { public: timer(time_of_day when, command & what); void run(); void cancel(); ... }; class command { public: virtual void execute() = 0; ... };
class turn_on : public command { public: explicit turn_on(heating_system & heating) : heating(heating) { } void execute() override { heating.turn_on(); } private: heating_system & heating; }; class turn_off : public command { public: explicit turn_off(heating_system & heating) : heating(heating) { } void execute() override { heating.turn_on(); } private: heating_system & heating; };
class turn_on : public command { public: explicit turn_on(heating_system & heating) : heating(heating) { } void execute() override { heating.turn_on(); } private: heating_system & heating; }; class turn_off : public command { public: explicit turn_off(heating_system & heating) : heating(heating) { } void execute() override { heating.turn_off(); } private: heating_system & heating; };
class timer { public: timer( time_of_day when, void execute(void * context), void * context); void run(); void cancel(); ... };
void turn_on(void * context) { static_cast<heating_system *>(context)->turn_on(); } void turn_off(void * context) { static_cast<heating_system *>(context)->turn_off(); }
class timer { public: timer(time_of_day when, function<void()> what); void run(); void cancel(); ... };
timer on( time_on, std::bind(&heating_system::turn_on, &heating)); timer off( time_off, std::bind(&heating_system::turn_off, &heating));
timer on(time_on, [&] { heating.turn_on(); }); timer off(time_off, [&] { heating.turn_off(); });
William Cook, "On Understanding Data Abstraction, Revisited" William Cook, "On Understanding Data Abstraction, Revisited"
paraskevidekatriaphobia, noun The superstitious fear of Friday 13th. Contrary to popular myth, this superstition is relatively recent (19th century) and did not originate during or before the medieval times. Paraskevidekatriaphobia (or friggatriskaidekaphobia) also reflects a particularly egocentric attributional bias: the universe is prepared to rearrange causality and probability around the believer based on an arbitrary and changeable calendar system, in a way that is sensitive to geography, culture and time zone. WordFriday.com
struct tm next_friday_13th(const struct tm * after) { struct tm next = *after; enum { daily_secs = 24 * 60 * 60 }; time_t seconds = mktime(&next) + (next.tm_mday == 13 ? daily_secs : 0); do { seconds += daily_secs; next = *localtime(&seconds); } while(next.tm_mday != 13 || next.tm_wday != 5); return next; }
std::find_if( ++begin, day_iterator(), [](const std::tm & day) { return day.tm_mday == 13 && day.tm_wday == 5; });
class day_iterator : public std::iterator<...> { public: day_iterator() ... explicit day_iterator(const std::tm & start) ... const std::tm & operator*() const { return day; } const std::tm * operator->() const { return &day; } day_iterator & operator++() { std::time_t seconds = std::mktime(&day) + 24 * 60 * 60; day = *std::localtime(&seconds); return *this; } day_iterator operator++(int) ... ... };
enum fizzbuzzed { fizz = -2, buzz = -1, fizzbuzz = 0, first = 1, last = 100 }; constexpr fizzbuzzed fizzbuzz_of(int n) { return n % 3 == 0 && n % 5 == 0 ? fizzbuzz : n % 3 == 0 ? fizz : n % 5 == 0 ? buzz : fizzbuzzed(n); }
When it is not When it is not necessary to necessary to change, it is change, it is necessary not to necessary not to change change. . Lucius Cary Lucius Cary
const const
&& &&
Immutable Value References to value objects are commonly distributed and stored in fields. However, state changes to a value caused by one object can have unexpected and unwanted side- effects for any other object sharing the same value instance. Copying the value can reduce the synchronization overhead, but can also incur object creation overhead. Therefore: Define a value object type whose instances are immutable. The internal state of a value object is set at construction and no subsequent modifications are allowed.
Copied Value Value objects are commonly distributed and stored in fields. If value objects are shared between threads, however, state changes caused by one object to a value can have unexpected and unwanted side effects for any other object sharing the same value instance. In a multi- threaded environment shared state must be synchronized between threads, but this introduces costly overhead for frequent access. Therefore: Define a value object type whose instances are copyable. When a value is used in communication with another thread, ensure that the value is copied.
class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... void set_year(int); void set_month(int); void set_day_in_month(int); ... };
Just because you have a getter, doesn't mean you should have a matching setter.
class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... void set(int year, int month, int day_in_month); ... }; today.set(2015, 9, 14);
class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... }; today = date(2015, 9, 14);
class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... }; today = date { 2015, 9, 14 };
class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... }; today = { 2015, 9, 14 };
"Get something" is an imperative with an expected side effect.
class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... };
class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int year() const; int month() const; int day_in_month() const; ... };
Conversions Overloading Derivation Genericity Mutability
Referential transparency is a very desirable property: it implies that functions consistently yield the same results given the same input, irrespective of where and when they are invoked. That is, function evaluation depends less—ideally, not at all—on the side effects of mutable state. Edward Garson "Apply Functional Programming Principles"
Asking a question Asking a question should not change should not change the answer. the answer. Bertrand Meyer Bertrand Meyer
Asking a question Asking a question should not change should not change the answer, and the answer, and nor should asking nor should asking it twice! it twice!