Re: [boost] [ANN] Phoenix-2 prerelease (Operators)

Joel de Guzman <joel@boost-consulting.com> writes:
Comments, feedback very welcome!
Operator
Lazy operators... ^^^^^^^^^^^^^^^^^ What is this doing here? What is the ellipsis for?
This facility provides a mechanism for lazily evaluating operators. Syntactically, a lazy operator looks and feels like an ordinary C/C++ infix, prefix or postfix operator. The operator application looks the same. However, unlike ordinary operators, the actual operator execution is deferred. Samples:
arg1 + arg2 1 + arg1 * arg2 1 / -arg1 arg1 < 150
We have seen the lazy operators in action (see Quick Start). Let's go back and examine it a little bit further: ^^ "them"
find_if(c.begin(), c.end(), arg1 % 2 == 1)
Through operator overloading, the expression arg1 % 2 == 1 actually generates a composite.
The fact that it's a composite (as opposed to a primitive) is of no interest at all to the user at this point. I would say "a function object" or "an actor." There's way too much emphasis on this whole composite/primitive distinction.
This composite object is passed on to STL's find_if function.
This is a place where you really see how not using any qualification hurts. Should be std::find_if, to distinguish it from the library's lazy find_if that can be passed to higher-order functions.
In the viewpoint of STL, the composite is simply a ^^ "From" function object expecting a single argument
, the container's element. ^^^^^^^^^^^^^^^^^^^^^^^
"of the container's value_type."
For each element in the container c, the element is passed ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "Each element in c" on as an argument arg1 to the composite (function object). The ^^^^ strike. That is not the name of the argument. composite checks if this is an odd value based on the expression arg1 % 2 == 1 where arg1 is iteratively replaced by the container's ^^^^^^^^^^^ ??? element.
A set of classes implement all the C++ free operators. ^^^^^^^ Really, the operators are implemented by a set of classes? Seems bizarre. Also seems irrelevant to the reader.
Like lazy functions (see function), lazy operators are not immediately executed when invoked. Instead, a composite (see composite) object is created and returned to the caller. Example:
(arg1 + arg2) * arg3
does nothing more than return a composite. A second function call will evaluate the actual operators. Example:
int i = 4, j = 5, k = 6; cout << ((arg1 + arg2) * arg3)(i, j, k);
will print out "54".
Arbitrarily complex expressions can be lazily evaluated following three simple rules:
That sentence doesn't make sense to me. "Can be" would apply to what the user can do, but the rules describe what the library does. Maybe "...are lazily evaluated according to these three rules:" I'm not sure that "Arbitrarily complex" adds anything. It would make sense in the context of what the user can do: "Arbitrarily complex epressions can be made lazy by following..." but then, that's incompatible with what follows.
1. Lazy evaluated binary operators apply when at least one of the operands is an actor object (see actors, primitives and composite). Consequently, if one of the operands is not an actor object, it is implicitly converted, by default, to a value (see values).
2. Lazy evaluated unary operators apply only to operands which are actor objects.
3. The result of a lazy operator is a composite actor object that can in turn apply to rule 1 and 2.
The actor applies to the rules, or the rules apply to the actor? This may seem like nitpicking, but the above can only make sense if you already know what it's saying, because you'll overlook the logical inconsistencies. It won't help the person who's trying to understand the library from the outside.
The one exception is the member pointer operator ->*, as we shall see later.
As long as you're spelling out the rules in detail, you should add the one exception immediately, so the user can read them all in one place.
Example:
-(arg1 + 3 + 6)
1. Following rule 1, lazy evaluation is triggered since arg1 is an actor (see primitives).
You have to say what part of the expression is being evaluated! In this case, arg1 + 3
2. The immediate right operand: 3 is implicitly converted to a value. Still following rule 1.
3. The result of this arg1 + 3 expression is a composite object, following rule 3.
4. Now since arg1 + 3 is a composite, following rule 1 again, its right operand: 6 is implicitly converted to a value.
5. Continuing, the result of arg1 + 3 ... + 6 is again another composite. Rule 3.
6. The expression arg1 + 3 + 6 being a composite, is the operand of the unary operator -. Following rule 2, the result is an actor object.
7. Following rule 3, the whole thing -(arg1 + 3 + 6) is a composite.
Confused?
If so, it's because you're exposing way more detail than the user needs, IMO. All this stuff could be better explained by a section titled How to know when the result of an expression is an actor
well, when in doubt, just wrap the operands in ref(x), val(x) or cref(x) appropriately and you'll be ok:
-(arg1 + val(3) + val(6))
How does the above help unconfuse the reader?
Lazy-operator application is highly contagious. In most cases, a ^^^^^^^^^^^^^^^^^^^^^^^^^ "Laziness" single argN actor infects all its immediate neighbors within a group (first level or parenthesized expression).
Take note that although at least one of the operands
of what?
must be a valid actor class in order for lazy evaluation to take effect, if this is not the case and we still want to lazily evaluate an expression, we can use ref(x), val(x) or cref(x) to transform the operand into a valid action object (see primitives). Example: ^^^^^^ actor
val(1) << 3;
Note that at least one operand of any operator must be a valid actor for lazy evaluation to take effect. To force lazy evaluation of an ordinary expression, we can use ref(x), val(x) or cref(x) to transform an operand into a valid actor object. For example, val(1) << 3; -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (1)
-
David Abrahams