$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
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