
On Sun, Apr 13, 2008 at 4:26 PM, dan marsden <danmarsden@yahoo.co.uk> wrote:
Hi All
The Egg review has run for 2 weeks now with zero reviews, which is obviously disappointing. There has been some good and pretty thorough discussion, and a couple of promises of reviews. As such, I'm extending the review period by 1 week until 20th April 2008.
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! :-) 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? 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? 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; 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. 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. 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. 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. 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. 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. 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< .. > { .. } 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. (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? 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"? 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. 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. Cordially, Daniel Walker