1[/============================================================================== 2 Copyright (C) 2001-2010 Joel de Guzman 3 Copyright (C) 2001-2005 Dan Marsden 4 Copyright (C) 2001-2010 Thomas Heller 5 6 Distributed under the Boost Software License, Version 1.0. (See accompanying 7 file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8===============================================================================/] 9 10[section Basics] 11 12[def __constant_n__ /n/] 13[def __argument_n__ a/n/] 14 15Almost everything is a function in the Phoenix library that can be evaluated as 16`f(a1, a2, ..., __argument_n__)`, where __constant_n__ is the function's arity, or number of arguments that the 17function expects. Operators are also functions. For example, `a + b` is just a 18function with arity == 2 (or binary). `a + b` is the same as `add(a, b)`, `a + b 19+ c` is the same as `add(add(a, b), c)`. 20 21[note Amusingly, functions may even return functions. We shall see 22what this means in a short while.] 23 24[heading Partial Function Application] 25 26Think of a function as a black box. You pass arguments and it returns something 27back. The figure below depicts the typical scenario. 28 29[$images/fbox.png] 30 31A fully evaluated function is one in which all the arguments are given. All 32functions in plain C++ are fully evaluated. When you call the `sin(x)` function, 33you have to pass a number x. The function will return a result in return: the 34sin of x. When you call the `add(x, y)` function, you have to pass two numbers x 35and y. The function will return the sum of the two numbers. The figure below is 36a fully evaluated `add` function. 37 38[$images/adder.png] 39 40A partially applied function, on the other hand, is one in which not all the 41arguments are supplied. If we are able to partially apply the function `add` 42above, we may pass only the first argument. In doing so, the function does not 43have all the required information it needs to perform its task to compute and 44return a result. What it returns instead is another function, a lambda function. 45Unlike the original `add` function which has an arity of 2, the resulting lambda 46function has an arity of 1. Why? because we already supplied part of the input: 47`2` 48 49[$images/add2.png] 50 51Now, when we shove in a number into our lambda function, it will return 2 plus 52whatever we pass in. The lambda function essentially remembers 1) the original 53function, `add`, and 2) the partial input, 2. The figure below illustrates a 54case where we pass 3 to our lambda function, which then returns 5: 55 56[$images/add2_call.png] 57 58Obviously, partially applying the `add` function, as we see above, cannot be 59done directly in C++ where we are expected to supply all the arguments that a 60function expects. That's where the Phoenix library comes in. The library 61provides the facilities to do partial function application. 62And even more, with Phoenix, these resulting functions won't be black boxes 63anymore. 64 65[heading STL and higher order functions] 66 67So, what's all the fuss? What makes partial function application so useful? 68Recall our original example in the [link phoenix.starter_kit.lazy_operators 69previous section]: 70 71 std::find_if(c.begin(), c.end(), arg1 % 2 == 1) 72 73The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a 74placeholder for an argument to be supplied later. Hence, since there's only one 75unsupplied argument, the lambda function has an arity 1. It just so happens that 76`find_if` supplies the unsupplied argument as it loops from `c.begin()` to 77`c.end()`. 78 79[note Higher order functions are functions which can take other 80functions as arguments, and may also return functions as results. Higher order 81functions are functions that are treated like any other objects and can be used 82as arguments and return values from functions.] 83 84[heading Lazy Evaluation] 85 86In Phoenix, to put it more accurately, function evaluation has two stages: 87 88# Partial application 89# Final evaluation 90 91The first stage is handled by a set of generator functions. These are your front 92ends (in the client's perspective). These generators create (through partial 93function application), higher order functions that can be passed on just like 94any other function pointer or function object. The second stage, the actual 95function call, can be invoked or executed anytime in the future, or not at all; 96hence /"lazy"/. 97 98If we look more closely, the first step involves partial function application: 99 100 arg1 % 2 == 1 101 102The second step is the actual function invocation (done inside the `find_if` 103function. These are the back-ends (often, the final invocation is never actually 104seen by the client). In our example, the `find_if`, if we take a look inside, 105we'll see something like: 106 107 template <class InputIterator, class Predicate> 108 InputIterator 109 find_if(InputIterator first, InputIterator last, Predicate pred) 110 { 111 while (first != last && !pred(*first)) // <--- The lambda function is called here 112 ++first; // passing in *first 113 return first; 114 } 115 116Again, typically, we, as clients, see only the first step. However, in this 117document and in the examples and tests provided, don't be surprised to see the 118first and second steps juxtaposed in order to illustrate the complete semantics 119of Phoenix expressions. Examples: 120 121 int x = 1; 122 int y = 2; 123 124 std::cout << (arg1 % 2 == 1)(x) << std::endl; // prints 1 or true 125 std::cout << (arg1 % 2 == 1)(y) << std::endl; // prints 0 or false 126 127 128[heading Forwarding Function Problem] 129 130Usually, we, as clients, write the call-back functions while libraries (such as 131STL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g. 132if you have to write an STL algorithm that takes in a predicate, or develop a 133GUI library that accepts event handlers, you have to be aware of a little known 134problem in C++ called the "__forwarding__". 135 136Look again at the code above: 137 138 (arg1 % 2 == 1)(x) 139 140Notice that, in the second-stage (the final evaluation), we used a variable `x`. 141 142In Phoenix we emulated perfect forwarding through preprocessor macros generating 143code to allow const and non-const references. 144 145We generate these second-stage overloads for Phoenix expression up to 146`BOOST_PHOENIX_PERFECT_FORWARD_LIMIT` 147 148[note You can set `BOOST_PHOENIX_PERFECT_FORWARD_LIMIT`, the predefined maximum perfect 149forward arguments an actor can take. By default, `BOOST_PHOENIX_PERFECT_FORWARDLIMIT` 150is set to 3.] 151 152 153[/ 154Be aware that the second stage cannot accept non-const temporaries and literal 155constants. Hence, this will fail: 156 157 (arg1 % 2 == 1)(123) // Error! 158 159Disallowing non-const rvalues partially solves the "__forwarding__" but 160prohibits code like above. 161] 162 163[heading Polymorphic Functions] 164 165Unless otherwise noted, Phoenix generated functions are fully polymorphic. For 166instance, the `add` example above can apply to integers, floating points, user 167defined complex numbers or even strings. Example: 168 169 std::string h("Hello"); 170 char const* w = " World"; 171 std::string r = add(arg1, arg2)(h, w); 172 173evaluates to `std::string("Hello World")`. The observant reader might notice 174that this function call in fact takes in heterogeneous arguments where `arg1` 175is of type `std::string` and `arg2` is of type `char const*`. `add` still works 176because the C++ standard library allows the expression `a + b` where `a` is a 177`std::string` and `b` is a `char const*`. 178 179[endsect] 180 181