On Sun, Feb 11, 2018 at 5:54 PM, Steven Watanabe via Boost < boost@lists.boost.org> wrote:
AMDG
On 02/11/2018 01:26 PM, Zach Laine via Boost wrote:
On Sat, Feb 10, 2018 at 6:01 PM, Steven Watanabe via Boost < boost@lists.boost.org> wrote:
[snip]
- "...any of the functions in Boost.YAP that takes" s/takes/take/
That's not wrong; "takes" needs to agree with "any", not "functions".
I think "any" is also plural when used as an indefinite pronoun.
It can go either way. I'm using it in the singular. The second definition in Google's online dictionary for "any" is: *2*. whichever of a specified class might be chosen.
tutorial/transform_matching.html:
- The negate example: struct xform { // This transform negates terminals. auto operator() (boost::yap::terminal_tag, double x) { return -x; }
// This transform removes negations. auto operator() (boost::yap::negate_tag, double x) { return x; } } seems somewhat confused. The behavior I would expect from this transform is that it can match expressions of the form `d` or `-d` and will return -d or d respectively.
This gets right at the point of the example. It's maybe a counterintuitive way to evaluate expressions, but the alternative is worse. The alternative is that terminal transforms are not auto-applied before a terminal is unwrapped and passed as a value to a tag-transform call. If that were how the library worked, there would be no way to write a tag-transform function that *did* apply the transform, even explicitly, because at that point the terminal is no longer visible.
I don't believe that that is worse than being unable to write a tag-transform that *doesn't* apply the transform. Nothing else in a transform implicitly recurses, so I think your choice here is inconsistent. In addition, it's not quite true that you can't apply it explicitly. I can think of at least three ways to do so: - re-wrap the argument in a terminal - call (*this)(terminal_tag(), d)
In either of those cases, any information you had about the details of the terminal are now gone, and you can't get them back. The value that you re-wrap or call as above may have come from a terminal that was an lvalue, an rvalue, a reference-expression, const, mutable, etc. - implement terminal evaluation in a separate
function which can be used as needed.
True. I had to make a decision about which convention violated my sense of surprise the least. This is the one I chose. It seems less surprising to get stuck with transforms you did not yet get to in the terminals (mainly because they happen only since you wrote those into your transform explicitly), than it does to get stuck because you lost important information about the properties of the unwrapped terminal. It may be that the community consensus is that my sense of surprise is wrong in this case; I'm open to being persuaded. [snip]
examples/calc1.html:
- "Here we can first see how much C++14-and-later language features help the end user -- the Proto version is much, much longer." I disagree with this assertion. The real reason that this is shorter than the Proto version is that you've baked placeholder substitution into evaluate, which I think is a mistake--are you building a lambda library or are you building a generic ET library?
evaluate() hardly qualifies as a lambda library! However, the point remains the same -- making yap::evaluate() is trivial with variadic templates, and would be necessarily limited in a C++98 library. Making such a trivial function part of the Yap library instead of asking the user to reinvent that same wheel in every Yap terminal use case seems like a feature to me, not a bug.
Positional placeholders are really only needed if you're doing lambda-like things. I don't think that the primary library interface should directly support domain-specific uses. I also don't think requiring those who actually need this to implement it themselves is an excessive burden, as a transform that does it should be <10 lines of code. If you really feel that it's necessary to provide this, just write the transform yourself and provide it along with the placeholders.
True enough. I hesitate to remove this feature because 1) it's already in there, and no else has to reinvent it when it's needed in their code, and 2) I don't think it does any real harm. Contrast this with the implicit transforms associated with evaluate(), which I think lead to bad code.
examples/calc2.html:
- This really misses the point of proto's calc2 example. An equivalent example with YAP would be to define a new ExpressionTemplate with an overloaded call operator.
I don't think this example missed the point.
The point of the example in Proto is how to add members to an expression type, not just how to make it callable. Wrapping it in another class is something quite different. Now, admittedly, Yap makes creating your own ExpressionTemplate types so easy, that this example is basically redundant...
Yeah, this last part is why I even left out some of the Proto examples.
In my calc2a example, I show the same functionality, just using lambdas to give the expressions names.
You can give the expressions names in Proto too:
BOOST_PROTO_AUTO(expr_1_fn, _1 + 2.0);
True. [snip]
examples/vector.html:
- return boost::yap::make_terminal(std::move(vec[n])); Move is probably wrong as you're working with a reference to begin with. (Of course, it doesn't really matter for double, but imagine that you have a vector
instead.) The move is required to force a copy; just because the reference is valid now doesn't mean it won't dangle later.
Sure, but when using the evaluate(transform()) idiom, you're guaranteed that the references returned by transform will not be left dangling.
Sure. But that's just in this example, and using that (admittedly dominant) idiom. I still want to reinforce in the example code how you make copies of value types, especially built-in ones.
But in the case of a vector
case, yes I would probably have done something different. examples/mixed.html:
- I think you'd be better off making sin_tag a regular function object like in the proto example.
Could you explain? It isn't immediately obvious to me what you mean.
struct sin_tag { template<class T> T operator()(const T& t) const { using std::sin; return sin(); } };
Then there's no need to use a yap-specific customization point (eval_call).
Oh, I see. Yeah, that's true. I just wanted to show how to do a transformed call I think. I'll add this alternate way of accomplishing the same thing to the example. Zach