Subject: Re: [boost] [local] Review
From: Thomas Heller (thom.heller_at_[hidden])
Date: 2011-11-20 23:16:35
On 11/20/2011 09:27 PM, Gregory Crosswhite wrote:
> On Nov 21, 2011, at 11:36 AM, Thomas Heller wrote:
>> I vote to not include Boost.Local into Boost for the following reason.
>> I obviously can not appreciate the value of local functions in C++.
> I similarly have trouble seeing the value of the higher-order functional programming constructs provided by Phoenix. Personally when I want to program in a functional programming style I write my code in a language Haskell rather than shoehorning functional programming into C++ constructs that were never designed for such a purpose. When I translate my Haskell code into C++ (as I have done for a numerical code I once wrote), I would never think of using Phoenix, but instead I would change the idioms that I was using to match C++'s stateful, imperative nature.
You should definitely take a look at spirit. Try to use Local in
> However, despite my criticisms I am in no way against the inclusion of Phoenix in Boost! I personally believe that programmers should have a toolbox with a broad selection of high-quality tools so that when a particular tool fits their problem, they can just grab it and make their lives easier. For this reason, although I do not use Phoenix myself or see myself using it in the foreseeable future to solve any of the problems that I face, I am happy to have it exist since clearly it is very useful for people who have need for such things as it provides --- and heck, maybe one day even I will stumble into a problem where it provides the best solution and be glad it is around. :-)
I absolutely agree. However ...
>> C++, IMHO, has better ways to structure code (namespaces and classes).
> Although you don't see any reason to use Boost.Local when we already have classes and namespaces, similarly Linus doesn't see any reason to use C++ when C has structs and function pointers. :-) That is to say, Boost.Local doesn't let you do something you couldn't do without it --- just like the functional programming libraries don't. However, it can make life easier when you are facing a problem where the most natural fit is a local function defined at the point where it is used.
... the proposed solution aren't real local functions, because you
explicitly need to capture variables in scope and ...
>> Therefor, Boost.Local is not an alternative to C++-Lambdas, Boost.Bind, Boost.Lambda or Boost.Phoenix, but to regular C++ functions
> I am perfectly happy to give you an admission that Boost.Local is not a complete alternative to C++-Lambdas, Boost.Bind, Boost.Lambda or Boost.Phoenix and hence should not be directly compared with them --- as long as you are no longer claiming that Boost.Local is made redundant by these other options. :-)
... I tried to distance myself from comparing Boost.Local to Bind,
Lambda and Phoenix, because the comparison just isn't fair. I try to see
them as an alternative to regular functions. And, TBH, it just doesn't
cut it. Mainly because of the macro overhead introduced.
>> That being said, I don't see Boost.Local as a complement to the other already existing functional programming style libraries. I see it as a complement to regular functions. It is true that this implementation of local functions leads to function objects that can be used as callbacks where something callable is expected.
> Yes, and the way that I see it that is Boost.Locals' major point of existing --- to make callbacks easier to write for those of us who want compatibility with C++03. And that is good enough for me. :-)
Right. TBH, I considered writing macros to reduce the boilerplate of
creating such construct for Phoenix. I decided to stop investigating
that because at the end of the day, the macros grew in complexity and
actually writing the boilerplate yourself turned out to be clearer,
shorter and more flexible.
>> However, that does not mean that they are higher order functions, which in turn means that they are not easily composable (that means, there is no way to compose two local functions to form another local function other than calling the macros again). Which has another side effect that they are completely incompatible with the existing solutions (ok, you can bind them again).
> Fair enough, but that is true of plain C++ functions and methods and we don't consider them made redundant by the functional programming libraries. :-)
> Nonetheless, I really do have to concede that until this discussion I never really thought of Phoenix as a functional programming library that just *happened* to also be useful for callbacks. While I still don't see a use for Phoenix myself in the foreseeable future, I am really glad that we have had this discussion because it has given me more insight as to what Phoenix is all about, and made it more likely that I would consider it in the future now that my understanding of its purpose has been clarified.
Well, when introducing phoenix, the main motivation is of course to
provide an easy to use mechanism to define callbacks for functions like
std::for_each. Turns out it can do a lot more.
>> Also, i think the verbosity of the macros defeat their initial purpose of "avoiding cluttering other parts of the program with functions, variables, etc unrelated to those parts".
> I think that you must misunderstand what is meant by "avoiding cluttering other parts of the program with functions, variables, etc unrelated to those parts". The point is that the function is declared at the point of use rather than elsewhere; the "verbosity of the macros" does not change this and hence could not defeat this purpose.
>> Additionally I can not see how local functions hide any information at all, the information is still visible (although hidden behind two enclosing, quite verbose, macro calls).
> Local functions hide information in two senses.
> First, they hide information in the same way that private members of classes do: although they are plainly visible in the code (and in particular the *public*-facing headers) you still can't easily get to them. (Of course, someone can technically mangle names to get access to the local functions and so they are not completely inaccessible, but likewise someone can use pointer tricks to get access to private variables and so in this sense local functions are not *less* hidden then private members.)
> Second, they keep the implementation details of a function "hidden" inside the function, so as not to provide noise *outside* the function to people who just want to treat the function as a black box. An additional advantage of this approach is that if a function needs to be copied or moved, you can just copy/move the body of the function as it its implementation is entirely self-contained.
Ok, i can live with that argument. However, i continue in claiming that
the whole noise hiding and locality argument is defeated by the
verbosity of the macros.
>> I admit the fact, that we as C++ Programmers need Macros to avoid repeating ourselves (aka boilerplate code). A lot of libraries do exactly this, and it is considered good practice. My main point of critique was that the Boost.Local seem to implement a quite complex logic and, basically, their own language. I would not consider the preprocessor the proper place to implement a DSEL.
> As I have said before, I find it somewhat difficult to believe that an author of *Phoenix* would find the Boost.Local DSL complicated. ;-) In my opinion, the Boost.Local DSL really isn't that all that complicated to use, though I will readily concede that at some point this becomes a matter of taste --- and as Lorenzo has said, taste is certainly a fair criteria to use in these reviews since we want all of the Boost libraries to be in good taste!
Sorry, I wasn't entirely clear. I am all for DSELs, I just think that
the PP is the wrong place. Admittingly, the complexity of functions is
in the body, not the arguments, so arcane errors with the proposed
library are more unlikely, but they do exist! I of course agree that the
errors that come out of any ET based code are horrible. But they show
exactly what went wrong, which is not so much the case when you do
something wrong in the calls to the macro. To use one of Lorenzo's
argument: "In my experience, this quickly becomes a non-issue after the
1st week that you start using the library." Which is true for all sorts
> As for the philosophical object to the heavy use of macros, I note that comparing Local which uses macros to Phoenix which uses template metaprogramming demonstrates a clear advantage of the macro approach: it provides error messages that can be understood by mere mortals. This is a feature that really can't be overstated, and if macros make it easier to provide this then I don't see the disadvantage of using them.
That is one of my points that i somehow can't convey:
You don't need macros for that. Just code up a regular off-line
function/function-object. Just try it, and you'll probably realize that
the boilerplate you need is negligible compared to the boilerplate code
you need to set up a local function.
>> Again, Lorenzo's effort and ingenuity of creating this very library is out of question, I solely believe that it is not the proper solution to this problem.
> We all appreciate your willingness to acknowledge this despite your personal distaste for the approach taken by Boost.Local. :-D
> Also, as I write earlier in this e-mail, while I strongly disagree with your arguments, the part of this discussion involving comparing Boost.Local to Boost.Phoenix has enlightened me as to what Boost.Phoenix is really about, for which I am greatly appreciative.
Well, we tried to keep Phoenix as close to C++ as possible and try to
advertise as a C++-in-C++ DSEL. Of course, a lot of improvements can be
made. One of which is to make it easier for users to use the "statement
syntax". Which you, BTW, already can. Just not in function scope, for
the reasons i tried to explain above. But this is not about Phoenix, but
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk