Boost logo

Boost :

Subject: Re: [boost] [Hana] Announcing Hana's formal review next week (June 10th)
From: Abel Sinkovics (abel_at_[hidden])
Date: 2015-06-21 05:03:28


Hi Louis,

On 2015-06-20 23:19, Louis Dionne wrote:
>> Why is string not a model of the Constant concept? I tend to think of
>> strings as values and since they contain characters only, they should be
>> representable at runtime. (the question is probably how they should be
>> represented).
> String used to be a model of Constant. The problem is that the `value`
> function, which extracts the value from the Constant, should preserve the
> semantics of the original Constant. However, since String's value was
> (formerly) a `char const*`, that structure was not preserved. For example,
> it would be true that
>
> "abcd"_s < "abcde"_s
>
> while
>
> value_of("abcd"_s) < value_of("abcde"_s)
>
> would not necessarily hold, because it would compare two `char const*`.
> The proper workaround would be to have a compile-time string class, and
> the `value()` of a Hana String should be a constexpr object of that type.
>
> However, if you need a `char const*` from a Hana String, you can use
> `to<char const*>("abcd"_s)`, which is a non structure-preserving conversion.
So if I get it right, a compile-time string class is missing to make it
a model of Constant.
I know that it can be converted into a const char*, but I think it would
be good in the long run to be able to think of strings as values. (as we
can do it in runtime code). I guess it can be added in a future version
of Hana, when such a compile-time string class becomes available.

>
>> What is the reason behind using tag dispatching in Hana? More
>> specifically, why is tag dispatching used instead of for example
>> enable_if? For example:
>>
>> template <class T, class = std::enable_if<is_a<Tuple, T>>>
>> auto head(T t);
>>
>> template <class T, class = std::enable_if<is_a<String, T>>>
>> auto head(T t);
>>
>> ...
>>
>> It is not clear to me why tag dispatching is preferred over this (or a
>> similar) approach. (eg. does tag dispatching perform better?) It might
>> be explained in the documentation.
> I wanted a two-layer dispatching system because the first layer (the
> functions you call) are actually function objects, which allows using them
> in higher-order algorithms. They can also do some compile-time sanity checks,
> which improves error messages.
>
> Now, we could just as well write:
>
> template <class T, class = std::enable_if<is_a<Tuple, T>>>
> auto head_impl(T t);
>
> template <class T, class = std::enable_if<is_a<String, T>>>
> auto head_impl(T t);
>
> and have head() call head_impl() after doing its sanity checks. However,
> when checking whether a type is a model of some concept, we basically check
> that some key functions are implemented. For example, `models<Iterable, T>`
> checks whether the is_empty, head and tail functions implemented for T. AFAIK,
> the only way to detect this with the above approach is to basically check
> whether the following expressions are valid in a SFINAE-able context:
>
> head_impl(std::declval<T>())
> is_empty_impl(std::declval<T>())
> tail_impl(std::declval<T>())
>
> But this requires doing the actual work. With tag dispatching, we can just
> ask whether head_impl<T>, is_empty_impl<T> and tail_impl<T> are defined, and
> nothing happens until we actually call head_impl<T>::apply(...). This is one
> of the reasons why I went with tag-dispatching, but frankly this is also a
> slightly arbitrary decision. Providing customization points is not exactly
> easy; there are a lot of different solutions. Also, the heterogeneous context
> definitely makes the task of providing customization points harder, since
> you can't rely on the actual type of the objects to dispatch.
I understand your point. (I think it might worth an entry in the
Rationales section of the doc). The reason why one might prefer
enable_if-based overloading is that based on my understanding of the
concepts lite proposal, using them one could create a Tuple concept (eg.
TupleC<T>()) checking "is_a<Tuple, T>" and then use it for overloading like

template <TupleC T>
auto head(T t);

or even:

auto head(TupleC t);

Which is a nice syntactic sugar for compile-time algorithms and could
make type-function overloading more "natural" for C++ programmers.
(However, the price for it is probably loosing all the benefits you have
explained above).

Regards,
   Ábel


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