1 [/==============================================================================
2 Copyright (C) 2001-2010 Joel de Guzman
3 Copyright (C) 2001-2005 Dan Marsden
4 Copyright (C) 2001-2010 Thomas Heller
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 ===============================================================================/]
12 [def __constant_n__ /n/]
13 [def __argument_n__ a/n/]
15 Almost 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
17 function expects. Operators are also functions. For example, `a + b` is just a
18 function 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)`.
21 [note Amusingly, functions may even return functions. We shall see
22 what this means in a short while.]
24 [heading Partial Function Application]
26 Think of a function as a black box. You pass arguments and it returns something
27 back. The figure below depicts the typical scenario.
31 A fully evaluated function is one in which all the arguments are given. All
32 functions in plain C++ are fully evaluated. When you call the `sin(x)` function,
33 you have to pass a number x. The function will return a result in return: the
34 sin of x. When you call the `add(x, y)` function, you have to pass two numbers x
35 and y. The function will return the sum of the two numbers. The figure below is
36 a fully evaluated `add` function.
40 A partially applied function, on the other hand, is one in which not all the
41 arguments are supplied. If we are able to partially apply the function `add`
42 above, we may pass only the first argument. In doing so, the function does not
43 have all the required information it needs to perform its task to compute and
44 return a result. What it returns instead is another function, a lambda function.
45 Unlike the original `add` function which has an arity of 2, the resulting lambda
46 function has an arity of 1. Why? because we already supplied part of the input:
51 Now, when we shove in a number into our lambda function, it will return 2 plus
52 whatever we pass in. The lambda function essentially remembers 1) the original
53 function, `add`, and 2) the partial input, 2. The figure below illustrates a
54 case where we pass 3 to our lambda function, which then returns 5:
56 [$images/add2_call.png]
58 Obviously, partially applying the `add` function, as we see above, cannot be
59 done directly in C++ where we are expected to supply all the arguments that a
60 function expects. That's where the Phoenix library comes in. The library
61 provides the facilities to do partial function application.
62 And even more, with Phoenix, these resulting functions won't be black boxes
65 [heading STL and higher order functions]
67 So, what's all the fuss? What makes partial function application so useful?
68 Recall our original example in the [link phoenix.starter_kit.lazy_operators
71 std::find_if(c.begin(), c.end(), arg1 % 2 == 1)
73 The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a
74 placeholder for an argument to be supplied later. Hence, since there's only one
75 unsupplied 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
79 [note Higher order functions are functions which can take other
80 functions as arguments, and may also return functions as results. Higher order
81 functions are functions that are treated like any other objects and can be used
82 as arguments and return values from functions.]
84 [heading Lazy Evaluation]
86 In Phoenix, to put it more accurately, function evaluation has two stages:
91 The first stage is handled by a set of generator functions. These are your front
92 ends (in the client's perspective). These generators create (through partial
93 function application), higher order functions that can be passed on just like
94 any other function pointer or function object. The second stage, the actual
95 function call, can be invoked or executed anytime in the future, or not at all;
98 If we look more closely, the first step involves partial function application:
102 The second step is the actual function invocation (done inside the `find_if`
103 function. These are the back-ends (often, the final invocation is never actually
104 seen by the client). In our example, the `find_if`, if we take a look inside,
105 we'll see something like:
107 template <class InputIterator, class Predicate>
109 find_if(InputIterator first, InputIterator last, Predicate pred)
111 while (first != last && !pred(*first)) // <--- The lambda function is called here
112 ++first; // passing in *first
116 Again, typically, we, as clients, see only the first step. However, in this
117 document and in the examples and tests provided, don't be surprised to see the
118 first and second steps juxtaposed in order to illustrate the complete semantics
119 of Phoenix expressions. Examples:
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
128 [heading Forwarding Function Problem]
130 Usually, we, as clients, write the call-back functions while libraries (such as
131 STL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g.
132 if you have to write an STL algorithm that takes in a predicate, or develop a
133 GUI library that accepts event handlers, you have to be aware of a little known
134 problem in C++ called the "__forwarding__".
136 Look again at the code above:
140 Notice that, in the second-stage (the final evaluation), we used a variable `x`.
142 In Phoenix we emulated perfect forwarding through preprocessor macros generating
143 code to allow const and non-const references.
145 We generate these second-stage overloads for Phoenix expression up to
146 `BOOST_PHOENIX_PERFECT_FORWARD_LIMIT`
148 [note You can set `BOOST_PHOENIX_PERFECT_FORWARD_LIMIT`, the predefined maximum perfect
149 forward arguments an actor can take. By default, `BOOST_PHOENIX_PERFECT_FORWARDLIMIT`
154 Be aware that the second stage cannot accept non-const temporaries and literal
155 constants. Hence, this will fail:
157 (arg1 % 2 == 1)(123) // Error!
159 Disallowing non-const rvalues partially solves the "__forwarding__" but
160 prohibits code like above.
163 [heading Polymorphic Functions]
165 Unless otherwise noted, Phoenix generated functions are fully polymorphic. For
166 instance, the `add` example above can apply to integers, floating points, user
167 defined complex numbers or even strings. Example:
169 std::string h("Hello");
170 char const* w = " World";
171 std::string r = add(arg1, arg2)(h, w);
173 evaluates to `std::string("Hello World")`. The observant reader might notice
174 that this function call in fact takes in heterogeneous arguments where `arg1`
175 is of type `std::string` and `arg2` is of type `char const*`. `add` still works
176 because the C++ standard library allows the expression `a + b` where `a` is a
177 `std::string` and `b` is a `char const*`.