
My review of Phoenix follows. I focus mainly on the documentation, because I've studied the Phoenix code-base on various occasions and have always found it to be very clear and of very high quality. That, combined with its extensive use in Spirit, makes me confident in the implementation of this library. I vote to accept Phoenix into Boost. More detailed review comments follow, but my vote to accept the library is not contingent on any changes. In the "Starter Kit", it would really help to state which namespaces (in general) the various functions like "val" and "ref" come from. I don't suspect that "ref" is boost::ref or C++0x's std::ref, but I could imagine that they could possible work with Phoenix. (As an aside: I bet that the references.cpp example here won't compile in C+ +0x, since ref() will be ambiguous). In the "Arguments subsection, second paragraph: "Arguments on the other hand, evaluation to an N-ary function. An argument represents the Nth argument." That's a bit confusing. Both those sentences and the example that follows make it look like argN always returns the last argument in the call, but that's not what happens. argI is an N-ary function that can be called with N >= I arguments and will return the Ith argument. Also in the "Arguments" subsection, there's a parenthetical comment "and it's BLL counterparts: _1, _2, _3, _4, and so on). IIRC, the _# placeholders actually came from Boost.Bind; the Lambda library originally used arg1, arg2, and arg3. In the "Lazy Functions" section, I'm wondering which protocol is being used for the nested "result" class template? Is it the result_of protocol or something else? This section needs a little more information, or a link, if users are going to be able to use this "function". You might also want to make clear that this is *not* std::function or boost::function, although either of those can be used to realize a monomorphic function from a Phoenix function. In the "Organization" section, it would be really, really cool if that library-organization picture was an image map linking each box to the documentation on that section. (But, this is just a fluff comment) In the "Actors" section, there's a note about the ability to set PHOENIX_LIMIT to something other than 10, but the word "macro" never shows up. Please tell the gentle reader that this is a macro. In "Predefined Arguments", nothing says that PHOENIX_ARG_LIMIT is a macro. In "Evaluating an Argument", first sentence, typo "from the those passed in". In "Extra arguments", spello "desireable". In "Composite", PHOENIX_COMPOSITE_LIMIT is certainly a macro, but the note doesn't say so. Also in "Composite", are we talking about 0..N actors or 0..N-1 actors? In the subsection "Operation", the phrase "the containers value_type" should be "the container's value_type". In the documentation for the "switch_ statement", I suggest pointing out explicitly that, to have multiple statements in a case_ or default_, the user will need to wrap the statements in an extra set of parentheses. The alternatively, of course, is to use a syntax like: case_<1>()[cout << val("one") << '\n', cerr << val("hello")], Interestingly, catch_<exception_type>()[ ... ] uses this same syntax; why the discrepancy between case_ and catch_? Or did I miss something? Anyway, now you have me trying to figure out how to tweak switch() to fully support break_() and fall-through from one case to another :) In the "let" documentation, the note about PHOENIX_LOCAL_LIMIT doesn't say that it's a macro/preprocessor constant. In the "Visibility" section, there's this example: let(_a = 1) [ let( _a = 1 , _b = _a // Ok. _a refers to the outer _a ) [ /*. body .*/ ] ] Personally, I would like to see this example be ill-formed. I know it isn't technically feasible to make bind the "_a" in "_b = _a" to the innermost _a, so instead I'd prefer if any use of a variable whose binding has changed within the same "let" would be an error. I *think* this can be done without too much pain through a static assertion, but feel free to ignore this suggestion if I'm wrong. Is "lambda" equivalent to BLL's "unlambda" or "protect"? It might help BLL-savvy users to point out the relationship. Also, BLL's documentation points out that one should use its "protect" when receiving a function object as a parameter. The Phoenix documentation for "lambda" indicates roughly the same thing, but it could be said more directly. For example, a note that advises users to lambda()[] any function object they receive from somewhere else. What level of interoperability can we expect among Boost.Bind, Phoenix, Boost.Lambda, and TR1/C++0x's bind, if any? - Doug

Doug Gregor wrote:
You might also want to make clear that this is *not* std::function or boost::function, although either of those can be used to realize a monomorphic function from a Phoenix function.
Actually, I'm not sure those can, since they don't expose the result protocol. They, however, should expose the result_of protocol, since they have a result_type.

Mathias Gaunard wrote:
Doug Gregor wrote:
You might also want to make clear that this is *not* std::function or boost::function, although either of those can be used to realize a monomorphic function from a Phoenix function.
Actually, I'm not sure those can, since they don't expose the result protocol.
Actually it can. typedef XXX result_type; is part of the original result protocol. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

On Wed, Sep 24, 2008 at 10:00 PM, Doug Gregor <dgregor@osl.iu.edu> wrote: <snip>
Is "lambda" equivalent to BLL's "unlambda" or "protect"?
as far as I can tell no, because boost::lambda::protect(f) is basically the identity, except that it changes the type of 'f', hiding its 'lambdiness'. OTOH phoenix::lambda[f] returns a stub that in turns returns 'f' (I didn't check if the type of 'f' is changed). The phoenix machinery makes sure that when used as a nested lambda expression, lambda[] actually work more or less like protect. The abstraction break if you use lambda[] as top level. For more on this topic, see my comments about phoenix on another mail. -- gpd

On Thu, Sep 25, 2008 at 2:54 PM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
On Wed, Sep 24, 2008 at 10:00 PM, Doug Gregor <dgregor@osl.iu.edu> wrote: <snip>
Is "lambda" equivalent to BLL's "unlambda" or "protect"?
as far as I can tell no, because boost::lambda::protect(f) is basically the identity, except that it changes the type of 'f', hiding its 'lambdiness'.
OTOH phoenix::lambda[f] returns a stub that in turns returns 'f' (I didn't check if the type of 'f' is changed). The phoenix machinery makes sure that when used as a nested lambda expression, lambda[] actually work more or less like protect. The abstraction break if you use lambda[] as top level.
For more on this topic, see my comments about phoenix on another mail.
Sorry for the confusion, I keep confusing protect and unlambda. I still find the need of adding another evaluation round to the result of lambda surprising. -- gpd

Doug Gregor wrote:
My review of Phoenix follows. I focus mainly on the documentation, because I've studied the Phoenix code-base on various occasions and have always found it to be very clear and of very high quality. That, combined with its extensive use in Spirit, makes me confident in the implementation of this library.
I vote to accept Phoenix into Boost.
Thank you!
More detailed review comments follow, but my vote to accept the library is not contingent on any changes.
Snipped some parts. I'll apply the suggestions and edits after the review.
Also in the "Arguments" subsection, there's a parenthetical comment "and it's BLL counterparts: _1, _2, _3, _4, and so on). IIRC, the _# placeholders actually came from Boost.Bind; the Lambda library originally used arg1, arg2, and arg3.
Hah! I didn't know that. I've always used arg1, arg2 but I didn't steal that from lambda. Lambda became a later influence after FC++.
In the "Lazy Functions" section, I'm wondering which protocol is being used for the nested "result" class template? Is it the result_of protocol or something else? This section needs a little more information, or a link, if users are going to be able to use this "function".
It's the original result protocol, but it will soon change to the result_of protocol as soon as I iron out some backward compatibility issues. Eric's proto port uses the result_of protocol.
You might also want to make clear that this is *not* std::function or boost::function, although either of those can be used to realize a monomorphic function from a Phoenix function.
Right.
In the "Organization" section, it would be really, really cool if that library-organization picture was an image map linking each box to the documentation on that section. (But, this is just a fluff comment)
A very good idea. More snips here on typos and spellos and the macros. I'll apply them.
Also in "Composite", are we talking about 0..N actors or 0..N-1 actors?
Right. It's N-1. Good catch.
In the documentation for the "switch_ statement", I suggest pointing out explicitly that, to have multiple statements in a case_ or default_, the user will need to wrap the statements in an extra set of parentheses.
Right!
The alternatively, of course, is to use a syntax like:
case_<1>()[cout << val("one") << '\n', cerr << val("hello")],
Interestingly, catch_<exception_type>()[ ... ] uses this same syntax; why the discrepancy between case_ and catch_? Or did I miss something?
Yeah, the discrepancy... It's one of the things I'm not quite satisfied with. I do like the brackets [] but the empty parens stick out like a sore thumb to me. At one point, I used the [] for the cases, changing it later to the (). Fickle, ain't I? I'm open to suggestion. If we are to break the interface anyway, I'd follow what the community wants.
Anyway, now you have me trying to figure out how to tweak switch() to fully support break_() and fall-through from one case to another :)
Oh, that one! It's a tricky one to crack. Not much priority was given on that since no one really asks for it. To be honest, in terms of usability, I'd rather crack return_. I know it's doable, but it's always been a balance. I tend to shy away if the metaprogramming is getting quite involved. Not that I don't like the challenge. It's just that I want to tame down the compile times.
In the "Visibility" section, there's this example:
let(_a = 1) [ let( _a = 1 , _b = _a // Ok. _a refers to the outer _a ) [ /*. body .*/ ] ] Personally, I would like to see this example be ill-formed. I know it isn't technically feasible to make bind the "_a" in "_b = _a" to the innermost _a, so instead I'd prefer if any use of a variable whose binding has changed within the same "let" would be an error. I *think* this can be done without too much pain through a static assertion, but feel free to ignore this suggestion if I'm wrong.
Hmmm. Ok duly noted. Let me look into this. Yes, I think a static assertion can be easily done.
Is "lambda" equivalent to BLL's "unlambda" or "protect"?
Yes. Albeit a more powerful rendition of it.
It might help BLL-savvy users to point out the relationship. Also, BLL's documentation points out that one should use its "protect" when receiving a function object as a parameter. The Phoenix documentation for "lambda" indicates roughly the same thing, but it could be said more directly. For example, a note that advises users to lambda()[] any function object they receive from somewhere else.
Right. Noted.
What level of interoperability can we expect among Boost.Bind, Phoenix, Boost.Lambda, and TR1/C++0x's bind, if any?
First should be the unification of the placeholders. I hate it when I have to use different placeholders for different libraries when they are essentially doing the same thing. The proto port is a good step in that direction. Beyond that, I'd like to hear from you and the community what you want to expect. It's an open road. Phoenix has already done it's job as a Spirit sidekick. It's just now that it's actively venturing outside that sphere. I would love to have you and the community plot its future. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

Joel de Guzman wrote:
Doug Gregor wrote:
Also in the "Arguments" subsection, there's a parenthetical comment "and it's BLL counterparts: _1, _2, _3, _4, and so on). IIRC, the _# placeholders actually came from Boost.Bind; the Lambda library originally used arg1, arg2, and arg3.
Hah! I didn't know that. I've always used arg1, arg2 but I didn't steal that from lambda. Lambda became a later influence after FC++.
Lambda's original placeholders were free1, free2 and free3. _1, _2, _3 were added later as aliases. I think that it has never had _4 and above. Jaakko's Binder library - the precursor of Lambda - used free1 and free2. http://www.cs.utu.fi/BL/

On Sep 25, 2008, at 9:32 AM, Joel de Guzman wrote:
Doug Gregor wrote:
The alternatively, of course, is to use a syntax like: case_<1>()[cout << val("one") << '\n', cerr << val("hello")], Interestingly, catch_<exception_type>()[ ... ] uses this same syntax; why the discrepancy between case_ and catch_? Or did I miss something?
Yeah, the discrepancy... It's one of the things I'm not quite satisfied with. I do like the brackets [] but the empty parens stick out like a sore thumb to me. At one point, I used the [] for the cases, changing it later to the (). Fickle, ain't I?
I'm open to suggestion. If we are to break the interface anyway, I'd follow what the community wants.
Personally, I don't find the extra ()'s in case_<1>() all that horrible, and I think it's the more regular to have all of the statements in []. That said, unless something *else* is breaking the interface, this isn't the thing that should be first to break the interface.
Anyway, now you have me trying to figure out how to tweak switch() to fully support break_() and fall-through from one case to another :)
Oh, that one! It's a tricky one to crack. Not much priority was given on that since no one really asks for it. To be honest, in terms of usability, I'd rather crack return_. I know it's doable, but it's always been a balance. I tend to shy away if the metaprogramming is getting quite involved. Not that I don't like the challenge. It's just that I want to tame down the compile times.
I'm not sure it's generally useful, but it sounds like a fun metaprogramming challenge :)
In the "Visibility" section, there's this example: let(_a = 1) [ let( _a = 1 , _b = _a // Ok. _a refers to the outer _a ) [ /*. body .*/ ] ] Personally, I would like to see this example be ill-formed. I know it isn't technically feasible to make bind the "_a" in "_b = _a" to the innermost _a, so instead I'd prefer if any use of a variable whose binding has changed within the same "let" would be an error. I *think* this can be done without too much pain through a static assertion, but feel free to ignore this suggestion if I'm wrong.
Hmmm. Ok duly noted. Let me look into this. Yes, I think a static assertion can be easily done.
Assuming it's not a compile-time killer, of course.
What level of interoperability can we expect among Boost.Bind, Phoenix, Boost.Lambda, and TR1/C++0x's bind, if any?
First should be the unification of the placeholders. I hate it when I have to use different placeholders for different libraries when they are essentially doing the same thing. The proto port is a good step in that direction.
Yes, this is very important for usability.
Beyond that, I'd like to hear from you and the community what you want to expect. It's an open road. Phoenix has already done it's job as a Spirit sidekick. It's just now that it's actively venturing outside that sphere. I would love to have you and the community plot its future.
Well, I think Phoenix is the next-gen library to replace Boost.Lambda. It's not that Lambda is bad---it's quite solid code---but we've learned a lot about how to build FP libraries in C++, and I'm sure that Jaakko would love to be free of the maintenance burden :) So, my first suggestion would be to deprecate Lambda in the first Boost release where Phoenix is available, and remove it one or two releases afterward. Bind is trickier. Phoenix does cover all of Bind's functionality (as far as I can tell), but Bind current works on horribly broken compilers where Phoenix just won't go (and shouldn't). Bind is also extremely lightweight from a compile-time perspective, while Phoenix isn't quite so small. Perhaps Peter has a different opinion on this, but I think Bind and Phoenix will coexist in Boost for a very long time (and that's okay). - Doug

I vote to accept Phoenix into boost. It nearly duplicates nearly all of boost::lambda which I think is a good thing, and extends it (containers et.al.). In some ways I'll miss the old name but it's time to move on. (And I like the imagery of an older system burning and becoming new again.) On the code: With all of the changes coming in C++0X, this isn't the last round of changes. I'm hopeful that with a fully compiant C++0X compiler Phoenix VX will be clear to even a casual observer. :> And with Proto it's likely that this won't be such a difficult task as it was the first time. On syntax: Things like case<constexpr>() [statement] the only alternative is to put the statement in the "()", and I prefer the current style with "[]". It fits with for_, while_ etc. Missing the "()" will make for nasty error messages but it can't be helped. On boost::bind: I'm in agreement with Doug, it will be a while before that library can be combined with this one. (years) On Evaluation: Documentation only. I've looked in depth at Phoenix before and it was cleanly written. On Knowledge in problem Domain: Well Peter Higgley and I wrote an early lambda FP paper in 2000 for C++ Report. Jaakko and I wrote the first generation of Lambda & paper soon after. (Jaakko has been the sole maintainer since and generated the boost quality version, including documentation.) Thanks Jaakko! Yours, -Gary- ------------------ powellg@amazon.com -----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Doug Gregor Sent: Thursday, September 25, 2008 8:15 AM To: boost@lists.boost.org Subject: Re: [boost] Phoenix review On Sep 25, 2008, at 9:32 AM, Joel de Guzman wrote:
Doug Gregor wrote:
The alternatively, of course, is to use a syntax like: case_<1>()[cout << val("one") << '\n', cerr << val("hello")], Interestingly, catch_<exception_type>()[ ... ] uses this same syntax; why the discrepancy between case_ and catch_? Or did I miss something?
Yeah, the discrepancy... It's one of the things I'm not quite satisfied with. I do like the brackets [] but the empty parens stick out like a sore thumb to me. At one point, I used the [] for the cases, changing it later to the (). Fickle, ain't I?
I'm open to suggestion. If we are to break the interface anyway, I'd follow what the community wants.
Personally, I don't find the extra ()'s in case_<1>() all that horrible, and I think it's the more regular to have all of the statements in []. That said, unless something *else* is breaking the interface, this isn't the thing that should be first to break the interface.
Anyway, now you have me trying to figure out how to tweak switch() to fully support break_() and fall-through from one case to another :)
Oh, that one! It's a tricky one to crack. Not much priority was given on that since no one really asks for it. To be honest, in terms of usability, I'd rather crack return_. I know it's doable, but it's always been a balance. I tend to shy away if the metaprogramming is getting quite involved. Not that I don't like the challenge. It's just that I want to tame down the compile times.
I'm not sure it's generally useful, but it sounds like a fun metaprogramming challenge :)
In the "Visibility" section, there's this example: let(_a = 1) [ let( _a = 1 , _b = _a // Ok. _a refers to the outer _a ) [ /*. body .*/ ] ] Personally, I would like to see this example be ill-formed. I know it isn't technically feasible to make bind the "_a" in "_b = _a" to the innermost _a, so instead I'd prefer if any use of a variable whose binding has changed within the same "let" would be an error. I *think* this can be done without too much pain through a static assertion, but feel free to ignore this suggestion if I'm wrong.
Hmmm. Ok duly noted. Let me look into this. Yes, I think a static assertion can be easily done.
Assuming it's not a compile-time killer, of course.
What level of interoperability can we expect among Boost.Bind, Phoenix, Boost.Lambda, and TR1/C++0x's bind, if any?
First should be the unification of the placeholders. I hate it when I have to use different placeholders for different libraries when they are essentially doing the same thing. The proto port is a good step in that direction.
Yes, this is very important for usability.
Beyond that, I'd like to hear from you and the community what you want to expect. It's an open road. Phoenix has already done it's job as a Spirit sidekick. It's just now that it's actively venturing outside that sphere. I would love to have you and the community plot its future.
Well, I think Phoenix is the next-gen library to replace Boost.Lambda. It's not that Lambda is bad---it's quite solid code---but we've learned a lot about how to build FP libraries in C++, and I'm sure that Jaakko would love to be free of the maintenance burden :) So, my first suggestion would be to deprecate Lambda in the first Boost release where Phoenix is available, and remove it one or two releases afterward. Bind is trickier. Phoenix does cover all of Bind's functionality (as far as I can tell), but Bind current works on horribly broken compilers where Phoenix just won't go (and shouldn't). Bind is also extremely lightweight from a compile-time perspective, while Phoenix isn't quite so small. Perhaps Peter has a different opinion on this, but I think Bind and Phoenix will coexist in Boost for a very long time (and that's okay). - Doug _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Powell, Gary wrote:
I vote to accept Phoenix into boost. It nearly duplicates nearly all of boost::lambda which I think is a good thing, and extends it (containers et.al.).
Thank you, Gary. It seems I don;t have to reply to your email asking where to find the library :-) In some ways I'll miss the old name but it's time to move on. (And I like the imagery of an older system burning and becoming new again.) Lambda is a good name. In the roadmap I was proposing, I suggested Phoenix V3 to be named Lambda V2. Sure, I want the name Phoenix, but I can keep that as a sub-title. Also, I do not want to throw away Lambda's splendid docs. I'll certainly want them integrated and merge the pieces where appropriate.
On the code: With all of the changes coming in C++0X, this isn't the last round of changes.
Definitely. I'm hopeful that with a fully compiant C++0X compiler Phoenix VX will be clear to even a casual observer. :> And with Proto it's likely that this won't be such a difficult task as it was the first time. Let's hope so.
On syntax: Things like case<constexpr>() [statement] the only alternative is to put the statement in the "()", and I prefer the current style with "[]". It fits with for_, while_ etc. Missing the "()" will make for nasty error messages but it can't be helped.
Ok. Consistency is important.
On boost::bind: I'm in agreement with Doug, it will be a while before that library can be combined with this one. (years)
Ditto.
On Evaluation: Documentation only. I've looked in depth at Phoenix before and it was cleanly written.
Thank you.
On Knowledge in problem Domain: Well Peter Higgley and I wrote an early lambda FP paper in 2000 for C++ Report. Jaakko and I wrote the first generation of Lambda & paper soon after. (Jaakko has been the sole maintainer since and generated the boost quality version, including documentation.) Thanks Jaakko!
Jaakko has been instrumental and influential in the development of Phoenix and it's infrastructure library: Fusion. I owe Jaakko gratitude in all of my boost work. Tuple begat Fusion. Jaakko even helped in the implementation at one point (with Jeremiah Willcock). Phoenix2 was developed with intense exchanges from Jaaakko, structure, syntax and all. Thank you, Jaakko! Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

Doug Gregor wrote:
On Sep 25, 2008, at 9:32 AM, Joel de Guzman wrote:
Doug Gregor wrote:
The alternatively, of course, is to use a syntax like: case_<1>()[cout << val("one") << '\n', cerr << val("hello")], Interestingly, catch_<exception_type>()[ ... ] uses this same syntax; why the discrepancy between case_ and catch_? Or did I miss something?
Yeah, the discrepancy... It's one of the things I'm not quite satisfied with. I do like the brackets [] but the empty parens stick out like a sore thumb to me. At one point, I used the [] for the cases, changing it later to the (). Fickle, ain't I?
I'm open to suggestion. If we are to break the interface anyway, I'd follow what the community wants.
Personally, I don't find the extra ()'s in case_<1>() all that horrible, and I think it's the more regular to have all of the statements in []. That said, unless something *else* is breaking the interface, this isn't the thing that should be first to break the interface.
V3 breaks new grounds and breaks the interface significantly. I'd still want to offer out-of-the-box backward compatibility but I don't want to stifle changes for the better just for backward compatibility. There's a way (I outlined one) to have a gradual, stress free, move from old to new. We've done it with Spirit2 without anyone noticing. We can do it again.
Anyway, now you have me trying to figure out how to tweak switch() to fully support break_() and fall-through from one case to another :)
Oh, that one! It's a tricky one to crack. Not much priority was given on that since no one really asks for it. To be honest, in terms of usability, I'd rather crack return_. I know it's doable, but it's always been a balance. I tend to shy away if the metaprogramming is getting quite involved. Not that I don't like the challenge. It's just that I want to tame down the compile times.
I'm not sure it's generally useful, but it sounds like a fun metaprogramming challenge :)
Yeah it is. I welcome anyone with some spare time to burn :-)
What level of interoperability can we expect among Boost.Bind, Phoenix, Boost.Lambda, and TR1/C++0x's bind, if any?
First should be the unification of the placeholders. I hate it when I have to use different placeholders for different libraries when they are essentially doing the same thing. The proto port is a good step in that direction.
Yes, this is very important for usability.
Beyond that, I'd like to hear from you and the community what you want to expect. It's an open road. Phoenix has already done it's job as a Spirit sidekick. It's just now that it's actively venturing outside that sphere. I would love to have you and the community plot its future.
Well, I think Phoenix is the next-gen library to replace Boost.Lambda. It's not that Lambda is bad---it's quite solid code---but we've learned
Lambda is very solid code.
a lot about how to build FP libraries in C++, and I'm sure that Jaakko would love to be free of the maintenance burden :) So, my first suggestion would be to deprecate Lambda in the first Boost release where Phoenix is available, and remove it one or two releases afterward.
Yeah, that's essentially the plan I outlined.
Bind is trickier. Phoenix does cover all of Bind's functionality (as far as I can tell), but Bind current works on horribly broken compilers where Phoenix just won't go (and shouldn't). Bind is also extremely lightweight from a compile-time perspective, while Phoenix isn't quite so small.
Some numbers (preprocessing): boost.bind 792K phoenix1.bind 853K phoenix2.bind 3700K Phoenix is not that heavy. The 3700K we see now is mostly due to the use of Boost.Fusion and Boost.MPL beneath it. If you are using those anyway, it's a win. What I find most compelling with Boost.Bind is that it just works on most compilers, even antiquated ones.
Perhaps Peter has a different opinion on this, but I think Bind and Phoenix will coexist in Boost for a very long time (and that's okay).
I agree. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

On Thu, Sep 25, 2008 at 5:14 PM, Doug Gregor <dgregor@osl.iu.edu> wrote:
On Sep 25, 2008, at 9:32 AM, Joel de Guzman wrote:
Doug Gregor wrote:
The alternatively, of course, is to use a syntax like: case_<1>()[cout << val("one") << '\n', cerr << val("hello")], Interestingly, catch_<exception_type>()[ ... ] uses this same syntax; why the discrepancy between case_ and catch_? Or did I miss something?
Yeah, the discrepancy... It's one of the things I'm not quite satisfied with. I do like the brackets [] but the empty parens stick out like a sore thumb to me. At one point, I used the [] for the cases, changing it later to the (). Fickle, ain't I?
I'm open to suggestion. If we are to break the interface anyway, I'd follow what the community wants.
Personally, I don't find the extra ()'s in case_<1>() all that horrible, and I think it's the more regular to have all of the statements in []. That said, unless something *else* is breaking the interface, this isn't the thing that should be first to break the interface.
Hum, what about relaxing a little the 'C++ in C++ tag' and making switch_ more powerfull than the builtin switch? Instead of: switch_(arg1) [ case_<1>(cout << val("one") << '\n'), case_<2>(cout << val("two") << '\n'), default_(cout << val("other value") << '\n') ] do this: switch_(arg1) [ case(1)[cout << val("one") << '\n'], case(2)[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ] this allows not only runtime selection of switch case values, but it will also work with everything that is equally comparable: switch_(arg1) [ case("one")[cout << val(1) << '\n'], case("two")[cout << val(2) << '\n'], default_[cout << val("other value"] << '\n') ] which IMHO is a killer feature. The fact that the case parameter is not a template parameter might limit compiler optimizations unless the the compiler is good at constant propagation, but there is a workaround: switch_(arg1) [ case(mpl::int_<1>())[cout << val("one") << '\n'], case(mpl::int_<2>())[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ] Let's leave the ugly syntax to those that need it :) As another extension, when visiting heterogeneous sequences (think fusion::for_each), a switch by type would be great: switch_(arg1) [ case(type<std::string>())[cout << val("std::string") << '\n'], case(type<int>()) [cout << val("int") << '\n'], default_[cout << val("other type"] << '\n') ] (in this case switch_ would need to know about type<> because it must not compile the not-taken branches). Even better: switch_(arg1) [ case(type<std::string>())[cout << val("std::string") << '\n'], case(type<std::vector<_> >()) [cout << val("a vector of something") << '\n'], default_[cout << val("other type"] << '\n') ] -- gpd

On Fri, Sep 26, 2008 at 4:53 PM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
do this:
switch_(arg1) [ case(1)[cout << val("one") << '\n'], case(2)[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ]
this allows not only runtime selection of switch case values, but it will also work with everything that is equally comparable:
switch_(arg1) [ case("one")[cout << val(1) << '\n'], case("two")[cout << val(2) << '\n'], default_[cout << val("other value"] << '\n') ]
which IMHO is a killer feature.
Blimey, yes, that really is worth having! BTW I assume 'case' is meant to read 'case_'? - Rob. -- ACCU - Professionalism in programming - http://www.accu.org

Giovanni Piero Deretta wrote:
Instead of:
switch_(arg1) [ case_<1>(cout << val("one") << '\n'), case_<2>(cout << val("two") << '\n'), default_(cout << val("other value") << '\n') ]
do this:
switch_(arg1) [ case(1)[cout << val("one") << '\n'], case(2)[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ]
this allows not only runtime selection of switch case values, but it will also work with everything that is equally comparable:
switch_(arg1) [ case("one")[cout << val(1) << '\n'], case("two")[cout << val(2) << '\n'], default_[cout << val("other value"] << '\n') ]
which IMHO is a killer feature.
In efficiency, you mean? One dispatch is 0(1) in the number of cases, the other is 0(n).

On Fri, Sep 26, 2008 at 6:24 PM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
Giovanni Piero Deretta wrote:
...<snip> which IMHO is a killer feature.
In efficiency, you mean? One dispatch is 0(1) in the number of cases, the other is 0(n).
Is that necessarily so? Surely specialisations for common cases could do some hashing and potentially achieve O(1)..O(n). Also, even in the templated case wouldn't the O(1) case only apply to contiguous integral cases.? - Rob.

Giovanni Piero Deretta wrote: [snip about the switch_]
Hum, what about relaxing a little the 'C++ in C++ tag' and making switch_ more powerfull than the builtin switch?
Instead of:
switch_(arg1) [ case_<1>(cout << val("one") << '\n'), case_<2>(cout << val("two") << '\n'), default_(cout << val("other value") << '\n') ]
do this:
switch_(arg1) [ case(1)[cout << val("one") << '\n'], case(2)[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ]
this allows not only runtime selection of switch case values, but it will also work with everything that is equally comparable:
switch_(arg1) [ case("one")[cout << val(1) << '\n'], case("two")[cout << val(2) << '\n'], default_[cout << val("other value"] << '\n') ]
which IMHO is a killer feature.
Cool!
The fact that the case parameter is not a template parameter might limit compiler optimizations unless the the compiler is good at constant propagation, but there is a workaround:
switch_(arg1) [ case(mpl::int_<1>())[cout << val("one") << '\n'], case(mpl::int_<2>())[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ]
Let's leave the ugly syntax to those that need it :)
It seems your proposal is compatible with this syntax anyway: case<1>()[cout << val("one") << '\n'], So you can choose one or the other.
As another extension, when visiting heterogeneous sequences (think fusion::for_each), a switch by type would be great:
switch_(arg1) [ case(type<std::string>())[cout << val("std::string") << '\n'], case(type<int>()) [cout << val("int") << '\n'], default_[cout << val("other type"] << '\n') ]
(in this case switch_ would need to know about type<> because it must not compile the not-taken branches). Even better:
switch_(arg1) [ case(type<std::string>())[cout << val("std::string") << '\n'], case(type<std::vector<_> >()) [cout << val("a vector of something") << '\n'], default_[cout << val("other type"] << '\n') ]
I love it! I think it's wonderful. Would you be interested in implementing this? :-P Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

On Sat, Sep 27, 2008 at 6:23 AM, Joel de Guzman <joel@boost-consulting.com> wrote:
Giovanni Piero Deretta wrote:
[snip about the switch_]
Hum, what about relaxing a little the 'C++ in C++ tag' and making switch_ more powerfull than the builtin switch?
Instead of:
switch_(arg1) [ case_<1>(cout << val("one") << '\n'), case_<2>(cout << val("two") << '\n'), default_(cout << val("other value") << '\n') ]
do this:
switch_(arg1) [ case(1)[cout << val("one") << '\n'], case(2)[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ]
this allows not only runtime selection of switch case values, but it will also work with everything that is equally comparable:
switch_(arg1) [ case("one")[cout << val(1) << '\n'], case("two")[cout << val(2) << '\n'], default_[cout << val("other value"] << '\n') ]
which IMHO is a killer feature.
Cool!
The fact that the case parameter is not a template parameter might limit compiler optimizations unless the the compiler is good at constant propagation, but there is a workaround:
switch_(arg1) [ case(mpl::int_<1>())[cout << val("one") << '\n'], case(mpl::int_<2>())[cout << val("two") << '\n'], default_[cout << val("other value"] << '\n') ]
Let's leave the ugly syntax to those that need it :)
It seems your proposal is compatible with this syntax anyway:
case<1>()[cout << val("one") << '\n'],
So you can choose one or the other.
As another extension, when visiting heterogeneous sequences (think fusion::for_each), a switch by type would be great:
switch_(arg1) [ case(type<std::string>())[cout << val("std::string") << '\n'], case(type<int>()) [cout << val("int") << '\n'], default_[cout << val("other type"] << '\n') ]
(in this case switch_ would need to know about type<> because it must not compile the not-taken branches). Even better:
switch_(arg1) [ case(type<std::string>())[cout << val("std::string") << '\n'], case(type<std::vector<_> >()) [cout << val("a vector of something") << '\n'], default_[cout << val("other type"] << '\n') ]
I love it! I think it's wonderful. Would you be interested in implementing this? :-P
Sure, I'll give it a try. -- gpd

Giovanni Piero Deretta wrote: [snip cool switch_ ideas]
I love it! I think it's wonderful. Would you be interested in implementing this? :-P
Sure, I'll give it a try.
Great! Phoenix3 should be the target though, I'm sure you agree :P I think it will be an awesome addition. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

My (brief) review of Phoenix. I vote to accept Phoenix into Boost, and in addition make the following comments. My vote is not dependent on addressing these comments. I have been using Phoenix for a short time (a few weeks), so cannot comment in the in-depth manner of Doug and others, but only give some first impressions as a user. The library addresses a much needed area of functionality, and seems to be conceptually cleaner then BLL. I have found understanding and using it fairly straight-forward. The only shortcomings I have encountered are in areas of documentation and ease of discovery. I would prefer to see Phoenix presented as an independent library, rather than as an aspect of Spirit. It is made clear in the documentation that Phoenix does stand alone, but I would prefer to see this made much clearer at higher level. I would like the equivalence of BLL's _1, _2 etc and Phoenix's arg1, arg2, etc to be made clearer in the documentation, and a clear statement whether Phoenix supports the _1, _2 style 'out-of-the-box'. Coming from a cold-start to investigate Phoenix, I found several different locations, eg, SourceForge, Boost Subversion, the vault, with old versions (I think - I never quite understood what was what!), so I'd like to see everything that's discarded deleted, and any relationships between what remains made clear. Finally, I should not let this opportunity pass without saying what a magnificent construction I consider Phoenix to be, despite any minor wrinkles that more rigorous investigation might reveal. - Rob.

Robert Jones wrote:
My (brief) review of Phoenix.
I vote to accept Phoenix into Boost, and in addition make the following comments.
Thank you, Robert.
My vote is not dependent on addressing these comments.
I have been using Phoenix for a short time (a few weeks), so cannot comment in the in-depth manner of Doug and others, but only give some first impressions as a user.
The library addresses a much needed area of functionality, and seems to be conceptually cleaner then BLL. I have found understanding and using it fairly straight-forward. The only shortcomings I have encountered are in areas of documentation and ease of discovery.
I would prefer to see Phoenix presented as an independent library, rather than as an aspect of Spirit. It is made clear in the documentation that Phoenix does stand alone, but I would prefer to see this made much clearer at higher level.
Yes. If Phoenix passes the review, it will be a full fledged Boost library. It's been long enough for it to be sitting in the shadow of Spirit. The eventual plan is for it to be the future Lambda. I'm hopeful that will happen after we iron out some issues like backward compatibility, etc.
I would like the equivalence of BLL's _1, _2 etc and Phoenix's arg1, arg2, etc to be made clearer in the documentation, and a clear statement whether Phoenix supports the _1, _2 style 'out-of-the-box'.
Ok. argN is historical, but if we are to assume Lambda 2.0, then I think it's best to move to _1 entirely. argN will still be supported.
Coming from a cold-start to investigate Phoenix, I found several different locations, eg, SourceForge, Boost Subversion, the vault, with old versions (I think - I never quite understood what was what!), so I'd like to see everything that's discarded deleted, and any relationships between what remains made clear.
Sorry about that. That will be fixed for sure. I'll do some cleanup after the review.
Finally, I should not let this opportunity pass without saying what a magnificent construction I consider Phoenix to be, despite any minor wrinkles that more rigorous investigation might reveal.
Thank you very much. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net
participants (7)
-
Doug Gregor
-
Giovanni Piero Deretta
-
Joel de Guzman
-
Mathias Gaunard
-
Peter Dimov
-
Powell, Gary
-
Robert Jones