# Thinking in Functions

CTO at MozaicLabs, Trainer & Mentor at Mozaic Works
Feb. 25, 2019
1 of 59

### Thinking in Functions

• 1. Thinking In Functions Alex Bolboacă,  @alexboly,  alex.bolboaca@mozaicworks.com February 2019
• 2. Why this talk
• 3. The Journey
• 4. A few years back
• 5. Last year… CppEurope Video available: https://www.youtube.com/watch?v=7L6oryyzZXc
• 6. Then… Book deal
• 7. About This Talk • A practical approach to use functional programming in C++ • Focused on essentials and the mindset • Not purist • No maths • Not touching on performance • My Goal: You should ﬁnd at least one technique you can use in your code
• 8. Core Ideas
• 9. Two Fundamental Ideas • Immutability • Functions are data
• 10. Why Immutability?
• 11. Why Immutability? The more obstacles one places in the translation between code and behavior, the more mistakes (aka bugs) we can make and the least effective we are as programmers.
• 12. Example of Mutable Code How many implementation options exist? int add(int& first, int& second){ ... }
• 13. Option 1 int add(int& first, int& second){ return first + second; }
• 14. Option 2 int add(int& first, int& second){ return first += second; }
• 15. Option 3 int add(int& first, int& second){ return second += first; }
• 16. What’s the Semantics? int add(int first, int second) int add(int first, int& second) int add(int& first, int second) int add(int& first, int& second) int add(int first, int* second) int add(int& first, int* second) int add(int* first, int* second) int add(int* first, int second) int add(int* first, int& second)
• 17. Immutable Code // If standalone function int add(const int& first, const int& second) // If part of a class int add(const int& first, const int& second) const How many implementation options?
• 18. Immutable Code // If standalone function int add(const int& first, const int& second){ return first + second; } // If part of a class int add(const int& first, const int& second) const{ return first + second; }
• 19. Why Immutability? Immutability simpliﬁes our translation effort, allowing your brain to focus on adding more behavior.
• 20. Functions Are Data
• 21. Not a New Idea In C++ • Function pointers • Functors
• 22. Let’s Represent Functions As Data // Lambda variable auto add = [](int first, int second){ return first + second; }; More details: http://en.cppreference.com/w/cpp/language/lambda
• 23. Careful: Lambdas can be mutable // Mutable auto increment = [](int& value){return ++value}; int aValue = 42; int incremented = increment(42); assert(incremented == 43); assert(aValue == 43)
• 24. Careful: Immutable Lambda • Function call operator is const • Parameters use their own speciﬁer // Immutable auto incrementImmutable = [](const int& value){ return value + 1; }; int aValue = 42; int incremented = increment(42); assert(incremented == 43); assert(aValue == 42)
• 25. Type Inference // Does not compile auto add(auto first, auto second){ return first + second; } // Works for any two values that have operator+ defined auto add = [](const auto& first, const auto& second){ return first + second; };
• 26. Value Capture Lambdas can capture values from the context. TEST_CASE(”type inference with lambdas”){ int first = 5; auto add = [=](const auto& second){ return first + second; }; CHECK_EQ(42, add(37)); CHECK_EQ(42.5, add(37.5)); }
• 27. Code Time Code Time
• 28. Consequences Functions are data. We can do operations on data. We can do operations on functions!
• 29. Operations on Functions
• 30. What is an Operation on Functions? Any operation that takes one (or more functions) and returns a new function. • Partial Application • Functional Composition • Currying • Higher level functions
• 31. Partial Application A function with n-1 parameters is obtained from a function with n parameters by binding one parameter to a value. using namespace std::placeholders; auto divide = [](const auto& first, const auto& second){ return first/second; }; int specialValue = 10; auto divideSpecialValue = bind(divide, specialValue, _1); // Equivalent with // auto divideSpecialValue = [](const auto& second) { // return 10 / second // };
• 32. Functional Composition Create a function h(x) from two functions f(y) and g(z), such that for any value of x, h(x) = f(g(x)). C++ doesn’t yet have a functional composition operator, so we have to write our own function: template<typename F, typename G> auto compose(F f, G g){ return [f, g](auto x){ return f(g(x)); }; }
• 33. Example auto increment = [](const auto& value){ return value + 1; }; auto incrementTwice = compose(increment, increment); /* Equivalent with: auto incrementTwice = [&](const auto& value){ return increment(increment(value)); }; */ CHECK_EQ(3, incrementTwice(1));
• 34. Currying Decompose a function with N arguments into N functions with one argument auto curriedAdd = [](const auto& first){ return [first](const auto& second){ return first + second; }; };
• 35. Currying in C++ Unfortunately, there’s no operator that supports currying. In pure functional languages, curry is done by default, and linked with partial application. For example: auto divide = [](const auto& first, const auto& second){ return first/second; }; divide(5) => a new function that takes second as parameter, equ
• 36. Interesting Fact Curry. Haskell Curry
• 37. Pass Functions as Parameters TEST_CASE(”pass functions as parameters”){ vector<int> values{42, 1, 55, 23}; vector<int> expected{1, 23, 42, 55}; auto compare = [](const auto& first, const auto& second){ return (first < second); }; sort(values.begin(), values.end(), compare); CHECK_EQ(expected, values); }
• 38. Short list of higher level functions Find them in or • ﬁnd_if • transform • reduce / accumulate • count_if • all_of / any_of / none_of • … See examples on https://github.com/MozaicWorks/functionalCpp
• 39. Code Time Code Time
• 40. Conclusions We can create functions from other functions through partial application, composition, currying. We can understand better the functions we write due to immutability.
• 41. Applying Functional Programming
• 42. Problem: TicTacToe Result Given a TicTacToe board that’s either empty or already has moves, print out the result of the game if the game ended or that the game is still in progress.
• 43. Approach 1. Clearly deﬁne the input; give examples 2. Clearly deﬁne the output; give examples 3. Identify a chain of functional transformations you can apply on the input data to turn it into the output data
• 44. Clearly Define the Output • Game not started • Game in progress • X won • O won • Draw
• 45. Clearly Define the Input Empty Board: _ _ _ _ _ _ _ _ _ X won by line: X X X O O _ _ _ _ etc.
• 46. Turns out that … X wins if • any line is ﬁlled with X OR • any column is ﬁlled with X OR • the main diagonal is ﬁlled with X OR • the secondary diagonal is ﬁlled with X
• 47. Transformations board -> collection(all lines, all columns, all diagonals) -> if any(collection, filledWithX) -> X won where filledWithX(line|column|diagonal L) = all(token on L equals 'X')
• 48. Remove duplication with functional operators • Functions with the same parameter? Partial application! Or classes! • Functions with the same structure? Higher level functions! • Chained calls? Function composition!
• 49. Code Time Code Time
• 50. What I’m Exploring Next
• 51. Refactoring code through Immutability Premise: • Any program can be written as Input -> Pure Functions -> State change (either UI or storage) • This property of the code applies on multiple levels, even on a single function
• 52. Refactoring Method • Take a complex piece of code: • extract a small piece of it as another method • extract everything mutable as parameter • until the method is immutable • check by making everything const and compiling • then decompose the long immutable function into smaller immutable functions • then recombine the functions into classes, based on the parameters they use
• 53. We End Up With Functional OOP • Data structures with set / get or public data • “Processor” objects that receive data and return other data • “Run and forget” objects: initialize, execute operations, throw it away • Separate, pluggable, input / output objects
• 54. Hypothesis Unconﬁrmed Hypothesis: this type of refactoring is safe even without tests. (Or, we can easily generate tests for the immutable function with property-based testing)
• 55. Conclusion Immutability and functions as data can simplify programming I invite you to try them out. Pick one and try it.
• 56. Learn more Functional programming in C++: https: //www.slideshare.net/alexboly/functional-programming-in-c-88970494 Hidden loops: https://www.slideshare.net/alexboly/hidden-loops Removing structural duplication: https://www.slideshare.net/alexboly/removing-structuralduplication