Boost logo

Boost :

Subject: Re: [boost] [metal] Feature Complete - Request for Feedback
From: Bruno Dutra (brunocodutra_at_[hidden])
Date: 2017-02-21 19:16:07


On Tue, Feb 21, 2017 at 4:32 PM, Peter Dimov via Boost <
boost_at_[hidden]> wrote:

> Klemens Morgenstern wrote:
>
>> Am 21.02.2017 um 14:11 schrieb Bruno Dutra via Boost:
>>
>> > This is by the way another advantage of Metal, since all of its types >
>> are precisely defined and friendly to pattern matching.
>>
>> I think that's your main selling point. Afaik Hana also relys on concepts
>> more than on actual types; i.e. the result types are not completely
>> specified.
>>
>
> This is a false dilemma; it's possible to take 'concepts' and return
> completely specified types.
>
> There are other legitimate reasons to prefer strictness in the arguments
> though - SFINAE friendliness is one. And there are reasons to prefer
> concept-ness. Such as for instance
>
> mp_transform<std::add_const_t, std::shared_ptr<X>> //
> std::shared_ptr<X const>
>

I'm glad you mentioned it, indeed there is no need for compromises there,
we can have both, but I forgot to discuss how Metal addresses this.

As a tool that makes it possible to drive overload resolution through
SFINAE, it was a very important design goal for Metal from the very
beginning that _everything_ it provides must be SFINAE friendly no matter
what. In the beginning the concepts Metal was based on were more flexible
and made it possible for example that any template specialization be used
as a List. That alone however opens a whole trunk of worms, because one has
to deal with stuff like this:

join<std::tuple<>, std::map<X, Y>, std::unique_ptr<Z>>

Should this be valid and if so what should be the result? One might think
that std::tuple<X, Y, Z> would be the obvious answer, but how about default
template parameters that both std::metal and std::unique_ptr have for their
trailing parameters? Should it then be std::tuple<X, Y, std::less<X>,
std::allocator<std::pair<X const, Y>>, Z, std::default_delete<Z>>? But why
should the result List "type" be std::tuple? Shouldn't it instead by
disallowed since the List "types" are not all the same?

Conundrums like these made it very tricky to implement even the simplest of
the algorithms and had a inevitable impact on performance. So I decided to
simplify the concepts to the minimum necessary to express their underlying
semantics, while providing helpers like metal::as_list and metal::as_number
that translate from List-like and Number-like things into strict Metal
Lists and Numbers. This proved to be the best design, because the
implementation simplified dramatically, performance improved and
metal::as_* helpers could be given maximum flexibility to convert the
widest variety of *-like things into their equivalents.

Your example is implemented in Metal like this

using _ = metal::transform<metal::lambda<std::add_const_t>,
metal::as_list<std::shared_ptr<X>>>; // metal::list<X const>
metal::apply<metal::lambda<std::shared_ptr>, _> // std::shared_ptr<X const>

Admittedly a bit verbosier, but not too bad.
.


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