2015-08-19 20:31 GMT-03:00 Eric Niebler
<snip>
I concur with Eric that functional composition is a killer feature and I strongly believe it should constitute the very core of any metaprogramming library. I just go a step further and greatly simplify things by getting rid of "metafunction classes" altogether. I've managed to transfer the entire burden of abstraction to an analogous of MPL's apply, which expects a "lambda expression" and a set of arguments and, through a handful of partial specializations, directly evaluates it. Atop apply<>, bind<> becomes a one-liner, as do everything else just as gracefully. Oh and this way one also avoids dealing with core issue #1430, since MPL's quote<> is no more.
Although interesting from a design perspective, I suspect that if you benchmark you'll find this approach is too heavy. Compile-time lambdas are expensive. Turning *every* metafunction evaluation into a lambda evaluation is going to kill compile-times.
Apologies if I've misunderstood.
Not every metafunction evaluation is made into a lambda evaluation, just
those that require higher order composability, such as fold, transform,
count_if, etc. In any case I'd like to discuss some numbers regarding the
following three *naive* implementations of transform<>, where extract<> is
an alias to "typename ::type", eval<> [1] is a SFINAE friendly metafunction
evaluator and apply<> behaves like its homonym from MPL, but uses eval<> as
its SFINAE backend.
template<
template
struct transform
struct apply
struct transform
If one looks closer, by doing the actual recursive metafunction
evaluation in a SFINAE context behind the scenes, apply<> becomes a monadic bind of metafunctions, which, from this perspective, are themselves nothing more than optionals. Such concepts bring great expressiveness to a functional world.
You refer to your eval template that returns just<Ret> or nothing? It's interesting, but essentially the same as turning evaluation failures into a SFINAE-able condition. It has the same pros and cons, too. If you get back a "nothing" from a complicated computation, you're left wondering why. I don't have a good solution to that yet.
What I mean is that by carefully using SFINAE to avoid internal errors one
can guarantee that instantiating any metafunction part of the library is
"safe", provided of course the user provides SFINAE friendly arguments. Now
allow me to elaborate on what I mean by "safe".
First let's consider the case where user provided arguments are SFINAE
friendly, i.e. user provided lambda, for instance, does not inherit from
some undefined/final/fundamental type or anything of the sort, then
instantiating any metafunction