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-19 16:52:46


Hi,

I've implemented a (very small) core of Metaparse using Hana to try the
library out. The aim was not to evaluate how such a base metaprogramming
library would affect the design of Metaparse, but to have a non-trivial
example to try the library out with.

As I have experience with compile-time only computations, I've been
focusing on that aspect of Hana.

A great thing about Hana is that it provides a syntax for template
metaprograms that is familiar for C++ developers without large overhead
for the compilation process. It makes functions over types look like
"regular" C++ functions.
However, this advantage might be a disadvantage in some cases. With the
Boost.MPL approach, the code evaluated at compile-time has a
significantly different syntax than the code executed at runtime. It is
easy to tell which is which, while with the new approach, this is not
(always) that simple, which might lead to confusion in more complex
code. So I have some concerns about it in larger metaprograms.

The library's design is strongly influenced by Haskell, which is in my
opinion a good thing given the functional nature of metaprogramming in C++.

Metaparse uses types that are built with Haskell's algebraic data-types
in mind. (Given a type and a number of constructors). This concept works
well with the "pattern matching" partial template specialisation
provides. I could represent such data-types in Hana (and the library
offers a few as well, eg. Optional is the Hana representation of
Haskell's Maybe). It would be useful to add a
step-by-step guide to the documentation for extending the library with
custom algebraic data-types - for example a "how to build your own
Optional/Either/etc" (Note: I was reading Optional's implementation as
an example to build my own data types).

Once I learned how to build my own data-types, I could easily create the
ones I needed and they worked intuitively (to me). So it seems to be
well supported and should be better documented.

It is great that the library makes it possible to have static if_ and
switch_ in the body of a function. I think a few things should be
pointed out about them (probably in the documentation):

- when entering a branch of an if_ (or switch_) you are calling a
different function which is not (always) the same as having a block
inside your function body. You might (and are very likely to) be
affected by how you pass arguments to this function (capture in the
lambda, pass by value, pass by rvalue reference, etc). This is something
to be aware of when using these constructs.

- if you use these structures (especially if you have 2 or 3 nested
layers of them in the same function), the same variable might have
different names on each level (eg. to make sure that a branch of the if_
gets instantiated only when it is needed and safe to do so). This can be
confusing.

- in some cases (and I have seen such cases in my small experiment) you
don't need lambdas. If calculating both branches of the if_ is always
safe and has no large overhead, you might just "inline" the two return
values. Eg. instead of

   if_(<condition>, []() { return 1_c; }, []() { return 2_c; })

   you can have

   if_(<condition>, 1_c, 2_c)

   This makes the code simpler. (My "real" example is inside a more
complex function, so I came up with this artificial example, I hope it
still demonstrates the idea).

Because of using if_ with lambdas (in most cases), I ended up leaving
almost every block in my functions with "return", so there were
"return"s all over my code. I just wanted to point this out, I have no
idea how to improve this. I guess it is something similar to having
"typename" everywhere in MPL-style metaprograms. :)

Even though I haven't tried it, but based on the documentation the Lazy
type seems to be an interesting way to approach lazy evaluation for
metaprograms. The idea of the lazy monad seems very interesting. One
thing I'd fix in Lazy's documentation: eval_if is mentioned in Lazy's
doc, however there is no link to it and it was not trivial to find
eval_if's doc. I'd make the places mentioning eval_if links to its
description.

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).

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 believe this library promotes a new and better way for building
metaprograms. Here are my answers to the formal review questions:

On 2015-06-05 21:18, Glen Fernandes wrote:
> We encourage your participation in this review. At a minimum, kindly
> state:
> - Whether you believe the library should be accepted into Boost
Yes.

> - Your name
Ábel Sinkovics

> - Your knowledge of the problem domain.
I have several years of template metaprogramming experience. I have
implemented MPL extensions supporting Haskell-style functional
programming (Mpllibs.Metamonad). I have no experience with heterogeneous
containers.

> You are strongly encouraged to also provide additional information:
> - What is your evaluation of the library's:
> * Design
Clean. It makes things (eg. datatype) explicit that have been implicitly
present in earlier metaprogramming libraries. Some design decisions
(pointed out above) should be better documented.

> * Implementation
I haven't looked at the implementation of the library in detail. I was
using the implementation of some parts as examples for extending the
library. The code was well segmented (which definitions/specialisations
implement what aspects) and easy to understand.

> * Documentation
I find the reference useful and the tutorial is explaning the concept of
the library (and the new metaprogramming technique) well.

However, I felt that the documentation could be extended with
step-by-step tutorials on how to do common things (eg. defining your new
data types, how to use things like if_, etc).

Multiple types are models of the Monad concept, however, the
documentation does not explain how the monadic operations are
implemented for the different types (eg. what chain is doing for lists,
etc). I believe it is not (always) trivial and should be explained in
the documentation (I had simliar things in Metamonad and decided to add
it to the documentation).

I really like it that for some elements (eg. Functor's transform
operation) the documentation contains benchmarks.

> * Tests
The library seems to be well covered with tests. I haven't looked at
them in detail.

> * Usefulness
I think the library is very useful as a metaprogramming base library to
build on top of. However, given its limited compiler support, libraries
built on top of it will probably have to wait until compilers catch up
before they can be widely adopted.

> - Did you attempt to use the library? If so:
Yes.

> * Which compiler(s)?
Clang 3.6.0

> * What was the experience? Any problems?
It was working, I had no problems.

> - How much effort did you put into your evaluation of the review?
I've read the tutorial and reimplemented the core of a template
metaprogramming library using Hana and tried a few other random things out.

Regards,
   Ábel


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