Eric Niebler
On 3/7/2015 6:41 AM, Louis Dionne wrote: [...]
FWIW, this implementation will quickly run afoul of core issue 1430[*], which I know you're aware of because you commented on a gcc bug about it. You should have a look at how I avoided the problem in Meta. meta::quote is somewhat subtle.
[*] http://open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1430
Lol, thanks. I've no such problems now with Hana (or I'm not aware of it).
In contrast, here is (in essence) how Meta defines quote:
template class f> struct quote { template
using apply = f ; }; In Meta, the template aliases are used extensively, and types evaluate directly to their results. Things are done eagerly. There are no "thunks".
Here's my understanding of how Meta works:
Meta still uses the classical concept of a metafunction with a nested type, but it is hidden behind `meta::eval`.
No. Metafunctions in Meta are incidental, not fundamental. If it were possible to specialize template aliases, there would be no nested ::type anywhere in Meta -- it's used as an implementation detail only for some of the algorithms. I use some tricks to *implement* Meta that I don't use when I'm *using* Meta. When using Meta, laziness is best (IMO) achieved with defer, lambda, and let (although nothing is stopping someone from creating metafunctions and using the meta::lazy namespace in a more "traditional" way).
Ok, I understand. It's mostly a philosophical difference, but one that's worth noting.
Of course, when writing a lambda or a let expression, evaluation needs to be deferred until the substitutions are made. I use a template called "defer" for that. It's only intended for use by let and lambda. Although it does give things a nested "::type", it doesn't strictly need to; indeed when I first added it, it didn't.
Anyway, that may seem like a subtle difference, but it feels like sea change to me. I find it much nicer working this way.
I don't find that defaulting to eager metafunctions is nicer working. It has been, at least for me, the source of a lot of pain because I was required to write helper metafunctions to branch lazily.
I never need to use helper metafunctions. Meta's expression evaluator handles laziness.
Quick question; is the expression evaluator compile-time efficient? In my experience, such expression evaluators end up being expensive at compile-time, especially if you sprinkle all your code with it. Of course, this _might_ only be relevant to hardcore computations, and in that case Hana will be slower anyway (with current compiler technology) so I'm just saying you might want to benchmark it for your own curiosity.
[...]
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.)
[...]
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.
It's an interesting feature, but I think even better would be to
give people the choice whether they want SFINAE-friendliness or not.
Anyway, implementing common_type turned out to be quite challenging
until I realized I could use the Maybe Monad to encode SFINAE, and
then common_type was just a monadic fold with the Maybe Monad. Here
you go:
using namespace boost::hana;
auto builtin_common_t = sfinae([](auto t, auto u) -> decltype(type<
std::decay_t