On Sun, Mar 8, 2015 at 2:56 PM, Eric Niebler <eniebler@boost.org> wrote: [snip] I feel like Meta's approach to laziness hasn't been understood. Here,
for instance, is a SFINAE-friendly implementation of std::common_type; it has a ::type when a common type exists, but otherwise it doesn't. When it was implemented with metafunctions it was a huge mess. With meta::defer and meta::let, it's simple and straightforward.
(NOTE: No metafunctions, no eval except to define common_type_t.)
namespace m = ranges::meta; namespace ml = ranges::meta::lazy;
template<typename T, typename U> using builtin_common_t = decltype(true? std::declval<T>() : std::declval<U>()); template<typename T, typename U> using lazy_builtin_common_t = m::defer<builtin_common_t, T, U>;
template<typename...Ts> struct common_type {};
template<typename ...Ts> using common_type_t = m::eval<common_type<Ts...>>;
template<typename T> struct common_type<T> : std::decay<T> {};
template<typename T, typename U> struct common_type<T, U> : m::if_c< ( std::is_same<decay_t<T>, T>::value && std::is_same<decay_t<U>, U>::value ), ml::let<lazy_builtin_common_t<T, U>>, common_type<decay_t<T>, decay_t<U>>> {};
template<typename T, typename U, typename... Vs> struct common_type<T, U, Vs...> : ml::let<ml::fold<m::list<U, Vs...>, T, m::quote<common_type_t>>> {};
// TESTS static_assert(std::is_same<common_type_t<char, short, char, short>, int>::value, ""); static_assert(std::is_same<common_type_t<char, short, float, short>, float>::value, "");
// HAS NO COMMON TYPE: static_assert(!m::has_type<common_type<int, int, int*>>::value, "");
What's interesting here is that you get a SFINAE-friendly common_type for free. Since Meta's expression evaluator is handling laziness, it can be SFINAE-friendly itself. Nowhere do you need to test whether a computation has succeeded or failed. If any substitution failure occurs in an immediate context, the whole computation is aborted. It just falls out of the lambda/defer interaction.
(You can get backtraces by moving computations into non-immediate contexts.)
I would be curious to see this implemented in Hana and in Turbo.
Yes! We need to see apples-to-apples comparisons of nontrivial, useful examples like this under the various libraries' approaches.
As for lazy branches, that can also be handled simply by let/defer:
// Test that the unselected branch does not get evaluated: template<typename T> using test_lazy_if_ = let<lazy::if_<std::is_void<T>, T, defer<std::pair, T> > >; static_assert(std::is_same<test_lazy_if_<void>, void>::value, "");
And don't forget this part! :) Zach