]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
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)`. | |
20 | ||
21 | [note Amusingly, functions may even return functions. We shall see | |
22 | what this means in a short while.] | |
23 | ||
24 | [heading Partial Function Application] | |
25 | ||
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. | |
28 | ||
29 | [$images/fbox.png] | |
30 | ||
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. | |
37 | ||
38 | [$images/adder.png] | |
39 | ||
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: | |
47 | `2` | |
48 | ||
49 | [$images/add2.png] | |
50 | ||
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: | |
55 | ||
56 | [$images/add2_call.png] | |
57 | ||
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 | |
63 | anymore. | |
64 | ||
65 | [heading STL and higher order functions] | |
66 | ||
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 | |
69 | previous section]: | |
70 | ||
71 | std::find_if(c.begin(), c.end(), arg1 % 2 == 1) | |
72 | ||
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 | |
77 | `c.end()`. | |
78 | ||
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.] | |
83 | ||
84 | [heading Lazy Evaluation] | |
85 | ||
86 | In Phoenix, to put it more accurately, function evaluation has two stages: | |
87 | ||
88 | # Partial application | |
89 | # Final evaluation | |
90 | ||
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; | |
96 | hence /"lazy"/. | |
97 | ||
98 | If we look more closely, the first step involves partial function application: | |
99 | ||
100 | arg1 % 2 == 1 | |
101 | ||
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: | |
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 | ||
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: | |
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 | ||
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__". | |
135 | ||
136 | Look again at the code above: | |
137 | ||
138 | (arg1 % 2 == 1)(x) | |
139 | ||
140 | Notice that, in the second-stage (the final evaluation), we used a variable `x`. | |
141 | ||
142 | In Phoenix we emulated perfect forwarding through preprocessor macros generating | |
143 | code to allow const and non-const references. | |
144 | ||
145 | We 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 | |
149 | forward arguments an actor can take. By default, `BOOST_PHOENIX_PERFECT_FORWARDLIMIT` | |
150 | is set to 3.] | |
151 | ||
152 | ||
153 | [/ | |
154 | Be aware that the second stage cannot accept non-const temporaries and literal | |
155 | constants. Hence, this will fail: | |
156 | ||
157 | (arg1 % 2 == 1)(123) // Error! | |
158 | ||
159 | Disallowing non-const rvalues partially solves the "__forwarding__" but | |
160 | prohibits code like above. | |
161 | ] | |
162 | ||
163 | [heading Polymorphic Functions] | |
164 | ||
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: | |
168 | ||
169 | std::string h("Hello"); | |
170 | char const* w = " World"; | |
171 | std::string r = add(arg1, arg2)(h, w); | |
172 | ||
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*`. | |
178 | ||
179 | [endsect] | |
180 |