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
("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
>> auto head(T t); template
>> 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
>> auto head_impl(T t); template
>> 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
` 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