Boost logo

Boost :

Subject: Re: [boost] [Boost-users] [Fit] formal review starts today
From: Paul Fultz II (pfultz2_at_[hidden])
Date: 2016-03-09 13:47:10


On Wednesday, March 9, 2016 at 2:33:55 AM UTC-6, Vicente J. Botet Escriba
wrote:
>
> Le 09/03/2016 08:34, paul Fultz a écrit :
> >
> >
> >
> >
> >> On Tuesday, March 8, 2016 4:58 PM, Vladimir Batov <
> Vladimi..._at_[hidden] <javascript:>> wrote:
> >>> On 03/09/2016 08:43 AM, paul Fultz wrote:
> >>> ...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.
> >> 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.
> > 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.
> Yes, please, show us some examples.
>

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.
 

> >> "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.
> Some parts of the C++ community name a function object a functor. It ha
> nothing to be with the applicative, functor, monad in type classes.
> >
> >> 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));
> Maybe it is worth noting that these is a C++17/20 proposal to manage
> with this case.
>
> [1] p0119r1 - Overload sets as function arguments
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0119r1.pdf

Thanks for the link, I will add a reference to that.
 

>
> >
> >> "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.
> 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).
> > 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);
> As others have noted you should separate the the high order function
> definition from the possibility to define them globally.
>
> 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); }
>

Alternatively, you could be written like this:

template<class... Ts>
constexpr auto for_each_tuple(Ts&&... xs) -> decltype(compose(unpack,
by)(std::forward<Ts>(xs)...))
{
    return compose(unpack, by)(std::forward<Ts>(xs)...);
}

Or some variation thereof. I should probably make a note about this
alternative definition, for those who don't like the macros and global
variables.
 

>
> 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

Yes, thats a good idea.
 

>
>
> >
> > 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<typename T, typename F, int... Is>
> > void for_each(T&& t, F f, seq<Is...>)
> > {
> > auto l = { (f(std::get<Is>(t)), 0)... };
> > }
> > }
> >
> > template<typename... Ts, typename F>
> > void for_each_tuple(std::tuple<Ts...> const& t, F f)
> > {
> > detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
> > }
> >
> > It is more code, with more C++ cleverness going on.
> This is s a good example, and of course will need to compare
> performances at compile time and run-time.
> >
> > Secondly, by making them objects, it allows us to decorate or enhance
> > functions. C++ doesn't have support for python-like decorators, so if
> you want
> > to enhance a function, it will need to be a function object through and
> > through.
> >
> >
> >> "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
> 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.
>

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.
 

> >> 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.
> Any reference to something that will appear in the future standard and
> describe that your macros are a way to emulate a future feature would be
> much appreciated. Could you elaborate how inline variables are related?
>

I'll try to find the papers that was written on them.
 

> >
> > 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.
> Not everyone needs constexpr. So maybe this use case should be moved to
> the advanced usage.
>

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.
 

> > 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.
> Only if the user needs to declare them globally.
>

Yes.
 

> > 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;
> > }
> I believe that there are a lot of users that would prefer to write the
> code that follows once a library provide the previous one
> > 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.
> Does it works with a MSVC C++14?
>

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.
 

> > 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.
> You need to let the user choose. If the user is using a C++14 compliant
> compiler, would it need the macros?

For lambdas yes. In C++17, all these macros will be unnecessary.
 

> If not it is not worth presenting it
> as something capital. This belong to the workaround and emulations and I
> could appreciate having those macros, but having the equivalent C++ code
> is better.
>

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.
 

> >
> >> 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...
> >
> > 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.
> Yes I've said not all the people knows about High order functional
> programming. You should explain and make reference to external resources
> as needed.
>

Yes, good point.
 

>
> Best,
> Vicente
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk