Boost logo

Boost :

Subject: Re: [boost] [yap] Review part 1: documentation
From: Zach Laine (whatwasthataddress_at_[hidden])
Date: 2018-02-12 16:45:38


On Sun, Feb 11, 2018 at 5:54 PM, Steven Watanabe via Boost <
boost_at_[hidden]> 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_at_[hidden]> 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<unique_ptr>
> >> 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<unique_ptr> 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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk