
On Sat, Feb 25, 2017 at 10:28 PM, Louis Dionne via Boost < boost@lists.boost.org> wrote:
[...]
Indeed there is something one can't do with Hana and that is exactly the very most important use case for Metal: control template overload resolution through SFINAE.
Consider the following very simple toy example.
~~~ #include <boost/hana.hpp> namespace hana = boost::hana;
template <typename X> constexpr auto fun(X const& x) -> decltype(void(hana::reverse(x)), true) { return true; }
constexpr bool fun(...) { return false; }
static_assert(fun(hana::tuple_t<>), ""); static_assert(!fun(0), ""); ~~~
[...]
To be fair, though, you can do this:
~~~ template<typename X, typename = std::enable_if_t<hana::Sequence<X>::value>> constexpr auto fun(X const& x) { return true; } ~~~
Here, `Sequence` is the concept required to have `hana::reverse`. In concept world, assuming a concept-ified Hana, you would write something like this instead:
~~~ template<hana::Sequence X> constexpr auto fun(X const& x) { return true; }
constexpr auto fun(...) { return false; } ~~~
Right, but that has the inconvenience of offloading explicit concept checking to the user, that better get it right or risk breaking overload resolution. BTW, you just gave us the perfect example of a use case for Metal: implementing such pseudo-concepts as hana::Sequence. Instead of trait-like, they could simply be made into a metafunction that triggers a substitution error with the help of Metal, such that one could get rid of std::enable_if and instead write the much more readable `typename = hana::Sequence<X>` directly. Also, by designing them such that they alias to std::integral_constant, they can also be used as traits if needed.
Also, I personally don't see the benefit of this SFINAE friendliness all the way down. In fact, I usually think that using a library that does that is more difficult, because when you mess up you don't get any information of why you messed up 20 instantiations down the stack. Instead, you just get a SFINAE failure at the top of the stack, with some back trace that may or may not be helpful to determine the cause of the initial failure.
That is very accurate in the context of heterogeneous contexpr algorithms, such as the ones implemented by Hana and one of the reasons why it is not surprising that Hana didn't take this path. In the context of template metaprogramming however it is the other way around. Errors triggered due to SFINAE unfriendly implementation details spill the obnoxious guts of template metaprogramming all over the error log and anyone that has played just a little bit with MPL can testify finding oneself scrolling through walls of incomprehensible nonsense trying in vain to pick up anything familiar that could hint to the underlying cause. SFINAE friendly interfaces however guarantee the error is triggered right there at the user facing API, placing the offending instantiation at the top of the error log, which points the user to the exact metafunction whose concept requirements have been violated, hiding away clever and very often not intuitive implementation details. Coupled with the concise concept system in Metal and an API that is precisely defined on top of them, the error can be promptly identified. If you are still skeptic about it, I invite you to try it out and see.
In any case, I do think that Metal (and Brigand) are useful libraries and Boost probably has room for one of those. I just don't think _this_ is the reason why.
I disagree, I've been playing with a library that implements heterogeneous algorithms on top of Metal as a way to find rough edges and polish them away before the API is frozen and I find the fact Metal is SFINAE friendly exceedingly useful to control overload resolution. I also found that it can be very useful to help reducing compile times, even if overload resolution can be unambiguously decided, by triggering substitution errors in the type signature of functions that are known not to be preferred, thus excluding potential viable overloads from the resolution step, which is way more onerous to the compiler. Bruno