shunsuke | 15 Apr 07:35
Picon

Re: Egg review period extended until April 20th

Daniel Walker wrote:
 > OK, I still don't want to give a formal review, because I haven't
 > looked at the whole library and I don't want to be unfair. However, I
 > sent some comments off-list to Shunsuke, and he asked me to post them
 > here. As he pointed out to me, it's the Boost way! :-)

Thank you, Daniel.

 > I really do think that Egg is technologically solid. If the list is
 > slow to show interest, I think that's not a reflection of some
 > technical concern, but perhaps it's a sign of usability hurdles... In
 > other words, how do you convince users that using the library would
 > make their lives easier? How do you sell the library?

I probably failed to sell the library.

 > Approaching the library for the first time, as a new user as much as a
 > potential reviewer, it seemed to me that Egg overlaps a great deal
 > with existing Boost libraries and with C++0x. I constantly found
 > myself asking: "Why would I want to learn this? Can't I just use bind?

egg::lazy is useful.
`lazy_plus(lazy_plus(_1, 3), lazy_plus(_2, _1))` is much more
readable than an expression using bind.

 > Yes, this is better than writing a function, but how is this any
 > better than just writing a class with operator()?" That last question
 > was the show stopper for me;

It depends on some conditions:
   * C++0x is available for you?
   * Your FunctionObject should be Polymorphic?

 > I don't have the time, energy or
 > inclination to learn a new paradigm for writing functors unless there
 > is a strong motivating need or big potential payoff. Remember,
 > fundamentally, the raison d'etre of Boost is to increase productivity.
 > At least, that's what the FAQ says. ;-) And in reality, if this wasn't
 > the case, no one would have ever started using Boost, it wouldn't be
 > famous today, and there would be no demand for Boost libraries to be
 > adopted into the standard. It's because of the Boost libraries' great
 > utility that they're so widely employed and admired.

According to my experience, directly or indirectly,
Egg has been increasing productivity.
FWIW, Egg is used to implement a Range Library:
   http://p-stade.sourceforge.net/oven/index.html

 > Each of the Egg components are interesting, but how are they helpful?
 > The name 'Egg' is a great example of what I'm talking about. It's an
 > interesting name, but I can't tell from the name how the library would
 > be useful to me.

"egg" didn't show a bad impression in [interest] phase,
so I kept the name.

 > The same is true for many of Egg's "selling points,"
 > for lack of a better term. For example, static initialization is
 > interesting, but in the highly unusual circumstance where I would find
 > myself using global or static objects of any sort (let alone
 > functors), I can already write "const my_functor f = my_functor()" and
 > most modern compilers will optimize away any significant run time
 > overhead. Also, as I understand it, supporting list initialization
 > does nothing to help the initialization order fiasco. That problem is
 > solved by avoiding constructor dependencies between static objects,
 > regardless of what sort of constructor they're built with. If I'm
 > wrong about these points, Shunsuke, you may want to make a
 > stronger/clearer explanation of the feature in the documentation. But
 > it could be that global/static functors are such an obscure use-case
 > that potential users simply won't care one way or the other. What
 > might be useful is to have some sort of global object manager, but
 > that's a different library, call it Boost.GOM ;-), and that doesn't
 > directly relate to functional programming.

I actually thought so.
(The old Egg supported only dynamic-initialization.)
I was upset when I saw a document about static-initialization in Proto.
I quote this: http://lists.boost.org/Archives/boost/2008/04/135452.php

 > Not that Egg shouldn't have a static initialization feature; it's just
 > to say that static initialization may not be a compelling selling
 > point. The same is true for the forwarding problem workarounds. The
 > by_ref strategy simply means passing arguments by reference, by_cref
 > means by const reference, etc. That's such a simple idea that it seems
 > convoluted to wrap it in some framework of forwarding strategies. I
 > just don't understand what it buys me. Whatever happened to
 > boost::call_traits? Would the forwarding strategies find a better home
 > there? Or are they really more of a backend detail? Maybe they should
 > be put in a special "Advanced Features" section of the documentation
 > for "power users." Either way, I don't feel like it's an attractive
 > selling point; to me it made me feel as if Egg requires you to jump
 > through hoops to do a simple thing like change function parameters
 > from refs to const refs.

I actually noted "You can skip reading Forwarding Strategies
when first reading the documentation." in QuickStart section.
Probably I should place it in top of ForwardingStrategies section.

 > On both of these points, I haven't thoroughly looked into the
 > documentation, the implementation, or played around with actual code
 > at all, which is part of the reason that I didn't think it would be
 > fair for me to write a review. However, I did play around with the
 > code snippet in the Overview section of the documentation. So, I
 > believe, here's how you would write my_plus using Egg:
 >
 > struct little_plus
 > {
 >    template<class Me, class X, class Y>
 >    struct apply
 >        : boost::remove_cv<X>
 >    { };
 >
 >    template<class Re, class X, class Y>
 >    Re call(X &x, Y &y) const
 >    {
 >        return x + y;
 >    }
 > };
 > typedef egg::function<little_plus> my_plus;
 >
 > ... and here's how you would write it normally:
 >
 > struct my_plus
 > {
 >    template<class> struct result;
 >    template<class Me, class X, class Y>
 >    struct result<Me(X,Y)>
 >        : boost::remove_cv<X>
 >    { };
 >
 >    template<class X, class Y>
 >    typename result<my_plus(X,Y)>::type
 >    operator()(X x, Y y) const
 >    {
 >        return x + y;
 >    }
 > };
 >
 > To my eye, I can't see how Egg makes things that much easier. It
 > doesn't look particularly helpful.

I was feeling annoyed that I had to write `typename result<my_plus(X,Y)>::type`.
Also, your `result<>` implementation is wrong.
It is error-prone to write ResultOf-conforming FunctionObject without some indirection.

 > Now, here's how you would write it
 > in C++0x:
 >
 > struct my_plus
 > {
 >    template<class X, class Y>
 >    typename boost::remove_cv<X>::type
 >    operator()(X x, Y y) const
 >    {
 >        return x + y;
 >    }
 > };
 >
 > So, as a new user, I'm going to spend my time learning C++0x! Why
 > should I instead sacrifice time learning Egg? If that question remains
 > unanswered for other potential users, then they will never get beyond
 > the introduction in the documentation, and you will attract no new
 > users.

It might depend on when C++0x will be available for users.
FWIW, many Egg components will be useful even in C++0x.

 > Meanwhile. there's all this great technology in Egg going to waste.
 > I'm not sure if the situation is as bad as I'm describing it, and if
 > so I'm not sure if I have any good suggestions to help. Perhaps, I
 > would again suggest looking at Boost.Iterator as an example.
 > Boost.Iterator has all sorts of gizmos, tricks, workarounds, etc. in
 > the backend; I get dizzy thinking about it! I don't want to know about
 > a single one of them, but I'm grateful for all of them. From my
 > perspective, as a user, I know std::iterator and the classic SGI
 > Iterator concepts. Then, I see Boost.Iterator makes these concepts
 > easier to program for (by providing archetypes to test against) and
 > more expressive (by expanding the number of concepts). Inheriting from
 > std::iterator is fragile and doesn't do much for you. Boost.Iterator
 > provides iterator_facade, so I inherit from it instead and get stuff
 > for free. Switching from std::iterator to iterator_facade is fairly
 > intuitive, and the new iterator classes are generally easier to write.
 > So, for all these reasons, I choose to learn and use Boost.Iterator.
 >
 > Maybe the key is that Boost.Iterator starts from something everyone is
 > already familiar with - the standard library's iterator utilities.
 > Since people are familiar with the standard and its shortcomings,
 > Boost.Iterator is more intuitive and accessible. So, maybe Egg should
 > start from the same place.
 >
 > In the Quick Start section of the documentation, as a motivating
 > example you use a code snippet to show the problems with function
 > templates. However, most of these problems are solved by the standard
 > AdaptableBinaryFunction concept. So, the question is not whether Egg
 > is more useful than a function template, but whether Egg is more
 > useful than an AdaptableBinaryFunction. So, your motivating example
 > should start not from ...
 >
 > template< .. >
 > .. make_filtered( .. )
 > {
 > ..
 > }
 >
 > ... but from ...
 >
 > template< .. >
 > struct make_filtered : std::binary_function< .. >
 > {
 > ..
 > }

You pointed an interesting comparison.
I could say "As std::iterator<> is fragile, std::unary/binary/nary_function<> is fragile.".
E.g. It would be difficult to express `make_filtered(make_filtered(src, &is_not_X), &is_not_Y)`
without PolymorphicFunctionObject concept.

 > Again, people are already familiar with the standard library and its
 > shortcomings. By framing your library in terms of solving problems
 > with the standard, rather than solving problems with static
 > initialization, the forwarding problem, result_of/lambda
 > compatibility, etc., I think your library would appear more useful.

Probably I should have emphasized advantages of PolymorphicFunctionObjects.
I assumed users were already familiar with it while using Boost.Lambda.

 > (The other more technical/mundane issues can be left in the backend;
 > users would be grateful for them without having to be specifically
 > aware of them.) Of course, the obvious problem with the standard is
 > that there's no std::nary_function<>. I think most C++ programmers can
 > relate to that problem. Can't function_facade be viewed as a solution?

The problem is that std::unary/binary/nary_function<> is not Polymorphic.
E.g. they are not usable when you write an overloaded function.

 > Ideally, you could start not only with the C++98 library but also with
 > TR1/C++0x. Boost.Function has already been accepted into TR1. Now,
 > couldn't the differed callback std::function<> be viewed as simply a
 > specific instance of an Egg "function adaptor"?

Yes. std::function<> can be regarded as one of the FunctionAdaptors.
It is similar to egg::mono, but it performs type-erasure.

 > I think it would be
 > exciting to see a new rewrite/drop-in-replacement/extension of
 > Boost.Function (or maybe call it Boost.Functor) that would move
 > function objects beyond both C++98 and C++0x. Egg could be a first
 > step in that direction. It's mostly a matter of refocusing your design
 > objectives and refactoring the code accordingly.

Sorry, I couldn't understand what you want.
Any (imaginary) example?

 > So, those are just some thoughts that occurred to me while I was
 > fiddling around with Egg over the weekend. In it's current state, I
 > have trouble getting excited about it. But I can see Egg has the
 > potential to become something that would appeal to me. Regardless, I
 > admire Shunsuke's openness and courage in submitting his work for
 > public scrutiny, and I hope my comments are helpful and encouraging in
 > at least some small way.

It is really helpful.

Regards,

--

-- 
Shunsuke Sogame

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


Gmane