1<!-- Copyright 2018 Paul Fultz II 2 Distributed under the Boost Software License, Version 1.0. 3 (http://www.boost.org/LICENSE_1_0.txt) 4--> 5 6Getting started 7=============== 8 9Higher-order functions 10---------------------- 11 12A core part of this library is higher-order functions. A higher-order function is a function that either takes a function as its argument or returns a function. To be able to define higher-order functions, we must be able to refer functions as first-class objects. One example of a higher-order function is `std::accumulate`. It takes a custom binary operator as a parameter. 13 14One way to refer to a function is to use a function pointer(or a member function pointer). So if we had our own custom `sum` function, we could pass it directly to `std::accumulate`: 15 16 int sum(int x, int y) 17 { 18 return x + y; 19 } 20 // Pass sum to accumulate 21 std::vector<int> v = { 1, 2, 3 }; 22 int total = std::accumulate(v.begin(), v.end(), 0, &sum); 23 24 25However, a function pointer can only refer to one function in an overload set of functions, and it requires explicit casting to select that overload. 26 27For example, if we had a templated `sum` function that we want to pass to `std::accumulate`, we would need an explicit cast: 28 29 template<class T, class U> 30 auto sum(T x, U y) 31 { 32 return x + y; 33 } 34 35 auto sum_int = (int (*)(int, int))∑ 36 // Call integer overload 37 int i = sum_int(1, 2); 38 // Or pass to an algorithm 39 std::vector<int> v = { 1, 2, 3 }; 40 int total = std::accumulate(v.begin(), v.end(), 0, sum_int); 41 42 43Function Objects 44---------------- 45 46A function object allows the ability to encapsulate an entire overload set into one object. This can be done by defining a class that overrides the call operator like this: 47 48 // A sum function object 49 struct sum_f 50 { 51 template<class T, class U> 52 auto operator()(T x, U y) const 53 { 54 return x + y; 55 } 56 }; 57 58There are few things to note about this. First, the call operator member function is always declared `const`, which is generally required to be used with Boost.HigherOrderFunctions.(Note: The [`mutable_`](/include/boost/hof/mutable) adaptor can be used to make a mutable function object have a `const` call operator, but this should generally be avoided). Secondly, the `sum_f` class must be constructed first before it can be called: 59 60 auto sum = sum_f(); 61 // Call sum function 62 auto three = sum(1, 2); 63 // Or pass to an algorithm 64 std::vector<int> v = { 1, 2, 3 }; 65 int total = std::accumulate(v.begin(), v.end(), 0, sum); 66 67Because the function is templated, it can be called on any type that has the plus `+` operator, not just integers. Futhermore, the `sum` variable can be used to refer to the entire overload set. 68 69Lifting functions 70----------------- 71 72Another alternative to defining a function object, is to lift the templated function using [`BOOST_HOF_LIFT`](/include/boost/hof/lift). This will turn the entire overload set into one object like a function object: 73 74 template<class T, class U> 75 auto sum(T x, U y) 76 { 77 return x + y; 78 } 79 80 // Pass sum to an algorithm 81 std::vector<int> v = { 1, 2, 3 }; 82 int total = std::accumulate(v.begin(), v.end(), 0, BOOST_HOF_LIFT(sum)); 83 84However, due to limitations in C++14 this will not preserve `constexpr`. In those cases, its better to use a function object. 85 86Declaring functions 87------------------- 88 89Now, this is useful for local functions. However, many times we want to write functions and make them available for others to use. Boost.HigherOrderFunctions provides [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) to declare the function object at the global or namespace scope: 90 91 BOOST_HOF_STATIC_FUNCTION(sum) = sum_f(); 92 93The [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) declares a global variable following the best practices as outlined in [N4381](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html). This includes using `const` to avoid global state, compile-time initialization of the function object to avoid the [static initialization order fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order), and an external address of the function object that is the same across translation units to avoid possible One-Definition-Rule(ODR) violations. In C++17, this can be achieved using an `inline` variable: 94 95 inline const constexpr auto sum = sum_f{}; 96 97The [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) macro provides a portable way to do this that supports pre-C++17 compilers and MSVC. 98 99Adaptors 100-------- 101 102Now we have defined the function as a function object, we can add new "enhancements" to the function. One enhancement is to write "extension" methods. The proposal [N4165](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf) for Unified Call Syntax(UFCS) would have allowed a function call of `x.f(y)` to become `f(x, y)`. Without UFCS in C++, we can instead use pipable function which would transform `x | f(y)` into `f(x, y)`. To make `sum_f` function pipable using the [`pipable`](/include/boost/hof/pipable) adaptor, we can simply write: 103 104 BOOST_HOF_STATIC_FUNCTION(sum) = pipable(sum_f()); 105 106Then the parameters can be piped into it, like this: 107 108 auto three = 1 | sum(2); 109 110Pipable function can be chained mutliple times just like the `.` operator: 111 112 auto four = 1 | sum(2) | sum(1); 113 114Alternatively, instead of using the `|` operator, pipable functions can be chained together using the [`flow`](/include/boost/hof/flow) adaptor: 115 116 auto four = flow(sum(2), sum(1))(1); 117 118Another enhancement that can be done to functions is defining named infix operators using the [`infix`](/include/boost/hof/infix) adaptor: 119 120 BOOST_HOF_STATIC_FUNCTION(sum) = infix(sum_f()); 121 122And it could be called like this: 123 124 auto three = 1 <sum> 2; 125 126In addition, adaptors are provided that support simple functional operations such as [partial application](https://en.wikipedia.org/wiki/Partial_application) and [function composition](https://en.wikipedia.org/wiki/Function_composition): 127 128 auto add_1 = partial(sum)(1); 129 auto add_2 = compose(add_1, add_1); 130 auto three = add_2(1); 131 132Lambdas 133------- 134 135Writing function objects can be a little verbose. C++ provides lambdas which have a much terser syntax for defining functions. Of course, lambdas can work with all the adaptors in the library, however, if we want to declare a function using lambdas, [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) won't work. Instead, [`BOOST_HOF_STATIC_LAMBDA_FUNCTION`](BOOST_HOF_STATIC_LAMBDA_FUNCTION) can be used to the declare the lambda as a function instead, this will initialize the function at compile-time and avoid possible ODR violations: 136 137 BOOST_HOF_STATIC_LAMBDA_FUNCTION(sum) = [](auto x, auto y) 138 { 139 return x + y; 140 }; 141 142Additionally, adaptors can be used, so the pipable version of `sum` can be written like this: 143 144 // Pipable sum 145 BOOST_HOF_STATIC_LAMBDA_FUNCTION(sum) = pipable([](auto x, auto y) 146 { 147 return x + y; 148 }); 149 150