Boost logo

Boost :

Subject: Re: [boost] [mpl] multiset
From: Zach Laine (whatwasthataddress_at_[hidden])
Date: 2015-03-09 13:40:52


On Sun, Mar 8, 2015 at 2:56 PM, Eric Niebler <eniebler_at_[hidden]> 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


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