Egg review period extended until April 20th

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. Please do post a review if you have any interest in this library. In case anybody is unsure, in the case of zero votes being posted, the default result will be a rejection. Please lets make sure we don't have to fall through to this default case. Introduction: It is not so easy to define Function Objects especially if you want to support Boost.ResultOf and Boost.Lambda. Therefore, as Boost provides iterator_facade and iterator adaptors, Egg provides function_facade and function adaptors. Egg provides the following features: * Workaround for the Forwarding Problem http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm * Helpers to build Function Objects: * egg::function and egg::function_facade provides the way to build Function Objects which supports Boost.ResultOf and Lambda. * Function Objects Adaptors(in other words, higher-order functions): * egg::curryN supports the currying. * egg::pipable emulates extension methods(C#). * egg::fuse/unfuse emulates variadic functions. * egg::nestN represents nested lambda expressions. * etc... The documentation is on line: http://p-stade.sourceforge.net/boost/libs/egg/ The zipped source code is in Vault/FunctionObjects: http://tinyurl.com/34lgda Tested under: Boost Development Trunk and Boost Version1.34.1. --------------------------------------------------- Please always state in your review, whether you think the library should be accepted as a Boost library! Additionally please consider giving feedback on the following general topics: - What is your evaluation of the design? - What is your evaluation of the implementation? - What is your evaluation of the documentation? - What is your evaluation of the potential usefulness of the library? - Did you try to use the library? With what compiler? Did you have any problems? - How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? - Are you knowledgeable about the problem domain? Thanks Dan Marsden Review Manager ___________________________________________________________ Yahoo! For Good helps you make a difference http://uk.promotions.yahoo.com/forgood/

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

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

On Tue, Apr 15, 2008 at 1:35 AM, shunsuke <pstade.mb@gmail.com> wrote:
Daniel Walker wrote: <snip>
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.
How is this result<> implementation wrong? I'm not convinced that writing result_of compatible functors is that error prone, but if I made a mistake here, that would pretty much prove your point. ;-) Also, I'm going to try to respond to your other comments as soon as I can. I've been doing a little reading, and I have some suggestions for refocusing/reframing a subset of the Egg problem domain in terms of the standard library and Boost.Function. At least, I hope to come up with some examples that are more concrete than I had in my original comments. Daniel
participants (3)
-
dan marsden
-
Daniel Walker
-
shunsuke