Re: [boost] [Boost-users] [Fit] formal review starts today

conflicts.
By global variables, I mean defined at the namespace level, not solely at the global namespace level. So functions using BOOST_FIT_STATIC_FUNCTION can be defined within a namespace. Also, the function objects are `const` so there isn't an issue from multithreaded environments.
local data I see no purpose for its existence.
Well, they can still be passed to constexpr functions, even if they are local, I probably shouldn't discuss BOOST_FIT_STATIC_LAMBDA in the Quick Start guide because its usage is not quite as a common.
library really does in terms of at least the main general functionality.
I usually start with the Quick Start guide with a library in order to get an understanding of what components are in the library and what they can do, then I start delving into the other components from there. Thats what I am showing in the Quick Start guide. I guess people have different ways of learning a library. I wonder what is needed to be explained better in a initial overview of the library.
arguments.
Yes, I am, so I probably need to make that more clear in the documentation.
easier for me and others to understand what 'by' does in this situation.
Yep, that part needs to go into more in-depth.
you enforce this view by your examples.
I'll try to do that.
I call that a function template.
Which is a family of functions.
ReturnType FunctionName(ZeroOrMoreFunctionParameters);
Yes, which can happen at class scope or namespace scope, and it can also be templated.

On 03/09/2016 08:43 AM, paul Fultz wrote:
Please do not take it a a criticism of any kind. That's just an impression I've got. I could be way off the mark.. I often am... I read the docs... twice... well, I tried... :-) I was not able to find an answer to a nagging question -- why I might need the library? What does it do that the standard C++ does not? To me the Quick Start felt more like Quick Start to Confusion. :-) Literally I felt like the deeper into the docs I was going the more alarmed I was. "We can make it (the functor) behave like a regular function" In all honesty I don't think I ever had such a need. Usually IMO the conversion is the other way around -- a reg. function into a functor. I think it happens pretty much automatically. "if we construct the class as a global variable" Should I be excited about it? Globals are often a royal pain. Do I want to construct it a a global variable? | "BOOST_FIT_STATIC_FUNCTION" | A macro? For C++14? Really? And given you mention it about 20 times just in the Quick Start section it seems quite pivotal for the library... Should I be excited about it? Macros are often a royal pain... Wrapping functors and lambdas in a macro?.. seems like I need quite a bit of convincing I might want that. For a library to be accepted the user has to understand the purpose/value of it and to get excited about it. I did not get it. In fact, I got the opposite... but I a not the brightest "bulb" in the pack... V. ||

the docs I was going the more alarmed I was.
Maybe I need a little more explanation of components in the Quick Start Guide. Perhaps, also, a comparison of writing some of the examples without using the library. The recursive print example is simple, but the technique could apply anytime you needed generic serialization of data. I have written code like that without using this library, so I can see benefit of using it for this particular case. So perhaps, writing a comparison without the library might make that clearer.
"We can make it (the functor) behave like a regular function"
I assume by functor, you mean function, as the library doesn't support functors and is beyond the scope of this library.
In all honesty I don't think I ever had such a need. Usually IMO the conversion is the other way around -- a reg. function into a functor. I
think it happens pretty much automatically.
Well, the library provides help for that as well, because currently in C++ you can't pass generic functions to other functions, like this: std::accumulate(v.begin(), v.end(), 0, std::max); // Compile error However, BOOST_FIT_LIFT will let you do that: std::accumulate(v.begin(), v.end(), 0, BOOST_FIT_LIFT(std::max));
"if we construct the class as a global variable"
Should I be excited about it? Globals are often a royal pain. Do I want
to construct it a a global variable? |
Whats the pain about these Global variables? They are const, can be
namespaced, and work just like free functions. However, they do have several
advantages.
First, by making them objects we can turn functions into first-class citizens.
This allows for functions to be easily passed around to other functions. In
fact, almost all the functions in this library are declared this way. This
make the functions(and adaptors) easily composable. For example, if you wanted
to write a function to do for_each over a tuple, you can easily compose the
`unpack` adaptor with the `by` adaptor:
BOOST_FIT_STATIC_FUNCTION(for_each_tuple) = compose(unpack, by);
So the `by` adaptor will call a function for each parameter, and the `unpack`
adaptor will unpack the elements of the tuple to each parameter. By composing
them together we can call a function for each element in a tuple. So, you can
write a function to print each value in a tuple, like this:
auto t = std::make_tuple(1, 2);
for_each_tuple([](auto x) { std::cout << x << std::endl; })(t);
So by passing functions to other functions, we can easily write some very
sophisticated functions without having to resort to metaprogramming. Just as a
comparison, here is how you could write for_each_tuple without this library:
namespace detail
{
template
convincing I might want that.
C++17 will be adding support for inline variables, so in the future this macro will be unnecessary. For now, it will take care of statically initializing the function and avoiding possible ODR issues. Furthermore, dealing with inconsistencies and bugs across multiple platforms is a real pain. For example, MSVC has lots of bugs with constexpr that can affect statically initializing the function object. So this macro provides workarounds so the it can be initialized statically. I don't see the macro as problematic, and without the macro is more problematic. However, you can write it without the macro like this: template<class T> struct static_const_storage { static constexpr T value = T(); }; template<class T> constexpr T static_const_storage<T>::value; template<class T> constexpr const T& static_const_var(const T&) { return detail::static_const_storage<T>::value; } static constexpr auto&& for_each_tuple = static_const_var(compose(unpack, by)); This of course, will only work on a fairly compliant C++11 compilers. It doesn't work on MSVC. Also, it won't work when using lambdas. No doubt, C++14 gets rid of a lot of macro usages, but C++14 is still lacking in many areas, so macros are still needed to fill in these gaps.
pack...
Thanks for the feedback, I probably should discuss more of the advantages of using function objects in the documentation to make it clear to more people.

On 2016-03-09 18:34, paul Fultz wrote:
I think that's an excellent idea.
Isn't it resolved with std::accumulate(v.begin(), v.end(), 0, boost::bind(std::max<int>, _1, _2)); Seems like std binders take care of the generic functions' "awkwardness". Am I missing anything?
I much prefer creating things locally to minimize the scope and the potential for mis-use. If I (or someone) create something (an object), there is a potential desire to stick some stateful info/data into it. Then, the complexity snow-balls as it becomes a bottleneck in multi-threaded env.
First, by making them objects we can turn functions into first-class citizens.
Why can't I use the standard binders to achieve that?.. Or what do I gain by using Fit instead?
skipped considerable chunk...
What I seem to hear is that Fit helps to overcome some plain-function limitations. I think you might want to consider highlighting what Fit adds compared to the standard binders. I felt that quite a few of your examples could be done using those binders. I might be wrong though. V.

On Wednesday, March 9, 2016 at 2:13:54 AM UTC-6, work wrote:
I do a comparison of something similar here: http://pfultz2.com/blog/2014/12/12/compare-overloading-2/
Well the bind should be unnecessary. You should be able to write: std::accumulate(v.begin(), v.end(), 0, &std::max<int>); I guess this is not the best example. In generic code, you may not know that the container is a container of ints, so by passing along `BOOST_FIT_LIFT(std::max)`, the types will be deduced. There are other cases where the types have to be deduced, because the type are either unknown to the callee or is called with different types. If it were an algorithm over tuples, not everything will be `int`, so its not as easy as writing `std::max<int>`.
Well you create function globally, and these are just functions, the same misuse that applies to functions applies here.
These functions are initialized at compile-time and must be ConstCallable, so if data is added to the functions it must be initialized without side-effects, and cannot mutate the data when the function is called.
Bind is a simple form of lambda expressions, so you can express a lot with lambda expressions. Futhermore, the library provides the lazy adaptor which works just like std::bind with the addition of being constexpr-friendly. However, there is a lot that you can't do with bind. You can't do overloading nor recursion nor projections with bind. Even though you can do composition and partial application, there can be complications when dealing with nested bind expressions. So sometimes, its better to have a component that can just handle composition or just handle partial application. Also, bind cannot handle variadiac parameters as well.
I think it would be could idea to compare the library to the capabilities of bind.

Le 09/03/2016 09:13, work a écrit :
std::accumulate(v.begin(), v.end(), 0, std::max); There is a proposal that will make this code correct. I believe that the closest to that is std::accumulate(v.begin(), v.end(), 0, OSAFA(std::max)); Where OSAFA mean Overload Set As Function Arguments [1] . The macro BOOST_FIT_LIFT tries to go in this direction. You can always continue to use bind, but I prefer to don't have to add the <int> annotation. As said before, I'm for the use of macros when we have a future language feature. We need to find a good name. A user is not forced to use the macro if this goes against its style or company coding rules/guidelines.
I believe that the library should NOT promote global functions or variables. The library should just provide some mechanism that help to define those global functions. IMO, most of the documentation should ignore all these STATIC macros.
I believe that we need to see the Fit library as two different concerns: HOF * provide some high order functions that are useful when doing functional programming SHOF * provide some mechanisms that help to define global high order functions You can ignore the second part (SHOF - static high order function) if you don't want to define global high order functions or if you prefer to define a function object to this end. The first part HOF, includes a lot of useful functions. IMHO the library need yet some modifications here and there, but I find that there is an added value for those that want to use functional programming.
I don't think you can do all with binders, but maybe I'm wrong. Bind is a way to transform a non high order function on one that is high-order. Maybe you can show how binding can be used to define some of the function the library provides, compose, fix, flow, ...match, partial, for example. We need to have more functions and ways to compose them. Fit goes in this direction. Best, Vicente [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0119r1.pdf

Le 09/03/2016 08:34, paul Fultz a écrit :
Yes, please, show us some examples.
[1] p0119r1 - Overload sets as function arguments http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0119r1.pdf
Note that we do that now with function objects and lambdas. This is a major argument that belongs to the motivation. You library works with and build high order functions (HOF).
auto for_each_tuple = compose(unpack, by); An alternative to use global function is to define function factories auto for_each_tuple() { return compose(unpack, by); } However this needs an extra level of parenthesis for_each_tuple()([](auto x) { std::cout << x << std::endl; })(t); Maybe it is worth talking of what is behind this style (point-free style or tacit programming) https://en.wikipedia.org/wiki/Tacit_programming
This is s a good example, and of course will need to compare performances at compile time and run-time.
I agree with that and I suggested Paul from the beginning to move this to an Advanced section. The fact that the library uses it to define the global function is of no concern for the users. Only if a user wants to define a global HOF, then those macros can help them. the advanced usage. AN alternative that you could or not have is to require better compilers. as e.g. Boost.Hana does. programming. You should explain and make reference to external resources as needed. Best, Vicente

On Wednesday, March 9, 2016 at 2:33:55 AM UTC-6, Vicente J. Botet Escriba wrote:
I do show some comparisons here: http://pfultz2.com/blog/2014/12/06/compare-overloading-1/ http://pfultz2.com/blog/2014/12/12/compare-overloading-2/ I should try to integrate them into the documentation.
Thanks for the link, I will add a reference to that.
Alternatively, you could be written like this:
template
Yes, thats a good idea.
Its not just needed for HOF, you need it to define any global function. I could show alternative way of defining them without the macro, using a free function. However, I would prefer to move the non-macro version to the advance section, since it a little more complicated, whereas the macro is much simpler.
I'll try to find the papers that was written on them.
However, the user needs constexpr, even if the function is not constexpr, if they want to initialize the variable statically.
AN alternative that you could or not have is to require better compilers. as e.g. Boost.Hana does.
However, I would like portability.
Yes.
It doesn't, or at least it is problematic. First, the variable does not have a unique address across translation units. Secondly, there is problems when this is combined with pre-compiled headers. And thirdly, as mentioned before, the constexpr bugs can make this not work at all. So the macro takes care of all these issues for the user, and I would rather not promote writing something that is not portable.
For lambdas yes. In C++17, all these macros will be unnecessary.
The macros are there so the user doesn't have to think about whether they need a workaround or emulation. I could add an advance section that explains how to some these things without a macro with a note about portability. The thing is I want something simple the user can start with when using the library. Explaining a construct that has caveats should go in the advance section. The macro does not have caveats.
Yes, good point.
participants (5)
-
paul Fultz
-
Paul Fultz II
-
Vicente J. Botet Escriba
-
Vladimir Batov
-
work