[interest] Egg, function object builders and adaptors

Hi, all. In a recent discussion, a library named Egg possibly left a good impression on boosters, so I propose it. Introduction: It is not so easy to define FunctionObjects 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: * Helpers to build FunctionObject: * egg::function and egg::function_facade provides the way to build FunctionObject which supports Boost.ResultOf and Lambda. * FunctionObject Adaptors(in other words, higher-order functions): * egg::curryN supports the currying. * egg::pipable emulates extension methods(C#). * egg::fuse emulates variadic templates in very limited way. * etc... The zipped source code is in Vault/FunctionObjects: http://tinyurl.com/34lgda The document is on line: http://p-stade.sourceforge.net/boost/libs/egg/ svn repository: https://p-stade.svn.sourceforge.net/svnroot/p-stade/trunk/boost Tested under: trunk and Boost1.34.1. An example of currying in Egg: int plus(int x, int y) { return x + y; } typedef result_of_curry2<int (*)(int, int)>::type T_curried_plus; T_curried_plus const curried_plus = BOOST_EGG_CURRY2(&::plus); void test() { BOOST_CHECK( curry2(&::plus)(4)(9) == 13 ); BOOST_CHECK( curried_plus(4)(9) == plus(4, 9) ); BOOST_CHECK( uncurry(curry2(plus))(4, 9) == 13 ); } If interested enough, it might be enqueued to the review queue. Anyway, comments are most welcome. Regards, -- Shunsuke Sogame

shunsuke wrote:
An example of currying in Egg:
int plus(int x, int y) { return x + y; }
typedef result_of_curry2<int (*)(int, int)>::type T_curried_plus; T_curried_plus const curried_plus = BOOST_EGG_CURRY2(&::plus);
The template type for the result of curry is a useful utility. Do you need the arity of the function in the name though? Can this not be done through template specialisation, or does that get too hairy on a few compilers? It might also be nice if it took the function type in the same way that boost::function does, i.e. int (int, int) rather than int (*)(int, int). And finally, does the curry function itself need to be a macro? Here is my implementation of curry2. I'd always assumed that a complete implementation would overload on the relevant "callable" types: template< typename R, typename V1, typename V2 > boost::function< R ( V2 ) > papply( boost::function< R ( V1, V2 ) > f, V1 v ) { return boost::bind( f, v, _1 ); } template< typename R, typename V1, typename V2 > boost::function< boost::function< R ( V2 ) > ( V1 ) > curry( boost::function< R ( V1, V2 ) > f ) { return boost::bind( papply< R, V1, V2 >, f, _1 ); } BTW, I implemented it like this to show the relationship between partial application and currying more clearly.
If interested enough, it might be enqueued to the review queue. Anyway, comments are most welcome.
I do hope so. I'm surprised that nobody has replied to queue it up already :( BTW, I really like the library name. I hope you keep it as is :) K -- http://www.kirit.com/

Kirit Sælensminde wrote:
typedef result_of_curry2<int (*)(int, int)>::type T_curried_plus; T_curried_plus const curried_plus = BOOST_EGG_CURRY2(&::plus);
The template type for the result of curry is a useful utility. Do you need the arity of the function in the name though? Can this not be done through template specialisation, or does that get too hairy on a few compilers?
FunctionObject can be overloaded with different arities. Another problem is that non-numbered form of macro is clearly impossible. But non-numbered function `curry` could be added for the famous FunctionObjects, e.g, function-pointer and boost::function<> etc.
It might also be nice if it took the function type in the same way that boost::function does, i.e. int (int, int) rather than int (*)(int, int).
result_of_curry2 takes any PolymorphicFunctionObject type. It seems not so good idea to admit function pointers as special case, because function pointers are a minority in FunctionObjects.
And finally, does the curry function itself need to be a macro?
When you need static-initialization. BOOST_EGG_CURRY2(..) is, in fact, a knotty braced initializer, which guarantees static-initialization. Of course, Egg supports also: typedef boost::result_of<T_curry2(int(*)(int,int))>::type T_curried_plus; T_curried_plus curried_plus = curry2(&::plus); No macros. But there is a function-call, which incurs dynamic-initialization. You might notice that "static form" using macro and "dynamic form" using function is nicely symmetric.
Here is my implementation of curry2. I'd always assumed that a complete implementation would overload on the relevant "callable" types:
As mentioned above, non-numbered form `curry` might support the famous "callables". It seems difficult to define "what is famous", though. Regards, -- Shunsuke Sogame

shunsuke wrote:
Kirit Sælensminde wrote:
And finally, does the curry function itself need to be a macro?
When you need static-initialization. BOOST_EGG_CURRY2(..) is, in fact, a knotty braced initializer, which guarantees static-initialization.
Of course, Egg supports also:
typedef boost::result_of<T_curry2(int(*)(int,int))>::type T_curried_plus; T_curried_plus curried_plus = curry2(&::plus);
No macros. But there is a function-call, which incurs dynamic-initialization. You might notice that "static form" using macro and "dynamic form" using function is nicely symmetric.
So the macro removed the first function call and actually writes a function which is the curried version rather than perform the currying at runtime? Now that *is* cool and seems to be a good enough reason to have a macro (to me at least)! :)
Here is my implementation of curry2. I'd always assumed that a complete implementation would overload on the relevant "callable" types:
As mentioned above, non-numbered form `curry` might support the famous "callables". It seems difficult to define "what is famous", though.
There is another Boost library out in 1.35, Boost.FunctionTypes, which might be able to help as it has decomposition functions that work with a wider range of callable function than for example Boost.Function does. Maybe that can be leveraged against? I've not had a chance to delve into it myself yet though, but Tobias Schwinger seemed to think it would handle the higher order requirements I was playing with last time I raised it on the list. http://thread.gmane.org/gmane.comp.lib.boost.user/30665/focus=30762 K -- http://www.kirit.com/

Kirit Sælensminde wrote:
Of course, Egg supports also:
typedef boost::result_of<T_curry2(int(*)(int,int))>::type T_curried_plus; T_curried_plus curried_plus = curry2(&::plus);
No macros. But there is a function-call, which incurs dynamic-initialization. You might notice that "static form" using macro and "dynamic form" using function is nicely symmetric.
So the macro removed the first function call and actually writes a function which is the curried version rather than perform the currying at runtime? Now that *is* cool and seems to be a good enough reason to have a macro (to me at least)! :)
Egg's ambition is to replace a normal function, which is statically-initialized. Hence, with or without great optimizers, Egg has to offer FunctionObject which is statically-initialized, I think.
There is another Boost library out in 1.35, Boost.FunctionTypes, which might be able to help as it has decomposition functions that work with a wider range of callable function than for example Boost.Function does. Maybe that can be leveraged against? I've not had a chance to delve into it myself yet though, but Tobias Schwinger seemed to think it would handle the higher order requirements I was playing with last time I raised it on the list.
http://thread.gmane.org/gmane.comp.lib.boost.user/30665/focus=30762
With that libray, I could document "When Egg can automagically calculate function arity". I bet clients use "numbered-form function" before they try to read my complex document, though. FWIW, Egg works with PolymorphicFunctionObject, which is virtually standardized by tr1 result_of. Regards, -- Shunsuke Sogame

Can somebody please provide a complete code example for this concept so that I can see what it's all about? Thanks, -Sid Sacek

Sid Sacek wrote:
Can somebody please provide a complete code example for this concept so that I can see what it's all about?
PolymorphicFunctionObject? In fact, most of all FunctionObjects are conforming PolymorphicFunctionObjects. Function-pointers, AdaptableFunctionObjects with which all the C++ programmers are familiar, FunctionObjects which Boost.Bind requires and returns, and Boost.Lambda Functors(with an extended header). When you want to build more complicated PolymorphicFunctionObject, Egg helps it. If you want to know how result_of and PolymorphicFunctionObjects collaborates, see: http://tinyurl.com/25hvn5 Regards, -- Shunsuke Sogame

Kirit Sælensminde wrote:
shunsuke wrote:
Kirit Sælensminde wrote:
And finally, does the curry function itself need to be a macro? When you need static-initialization. BOOST_EGG_CURRY2(..) is, in fact, a knotty braced initializer, which guarantees static-initialization.
Of course, Egg supports also:
typedef boost::result_of<T_curry2(int(*)(int,int))>::type T_curried_plus; T_curried_plus curried_plus = curry2(&::plus);
No macros. But there is a function-call, which incurs dynamic-initialization. You might notice that "static form" using macro and "dynamic form" using function is nicely symmetric.
So the macro removed the first function call and actually writes a function which is the curried version rather than perform the currying at runtime? Now that *is* cool and seems to be a good enough reason to have a macro (to me at least)! :)
Here is my implementation of curry2. I'd always assumed that a complete implementation would overload on the relevant "callable" types: As mentioned above, non-numbered form `curry` might support the famous "callables". It seems difficult to define "what is famous", though.
There is another Boost library out in 1.35, Boost.FunctionTypes, which might be able to help as it has decomposition functions that work with a wider range of callable function than for example Boost.Function does. Maybe that can be leveraged against? I've not had a chance to delve into it myself yet though, but Tobias Schwinger seemed to think it would handle the higher order requirements I was playing with last time I raised it on the list.
No. It's basically just type traits to adapt built-in, Callable types to sequential interfaces (so they can be handled regardless of their arity without using preprocessor metaprogramming). Further, as mentioned in my post you cited, it provides a workaround for certain strange portability issues. Regards, Tobias

Kirit Sælensminde wrote:
If interested enough, it might be enqueued to the review queue. Anyway, comments are most welcome.
I do hope so. I'm surprised that nobody has replied to queue it up already :(
For the record, I and a couple more, have expressed interest already in a previous thread. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

On Jan 17, 2008 10:42 AM, Joel de Guzman <joel@boost-consulting.com> wrote:
Kirit Sælensminde wrote:
If interested enough, it might be enqueued to the review queue. Anyway, comments are most welcome.
I do hope so. I'm surprised that nobody has replied to queue it up already :(
For the record, I and a couple more, have expressed interest already in a previous thread.
I didn't reply to this specific thread, but yes, count me definitely interested. -- gpd
participants (6)
-
Giovanni Piero Deretta
-
Joel de Guzman
-
Kirit Sælensminde
-
shunsuke
-
Sid Sacek
-
Tobias Schwinger