Boost logo

Boost :

Subject: Re: [boost] [Hana] Informal review request
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2015-03-07 12:53:43

Le 07/03/15 17:38, Louis Dionne a écrit :
> Vicente J. Botet Escriba <vicente.botet <at>> writes:
I would appreciate a comment about the TypeClass to Concept renaming

>> [...]
>> I appreciate what has been done already to improve the tutorial. Next
>> follows some comments about the design.
>> DataTypes and TypeClasses
>> ----------------------
>> I appreciate what has been done already. I have some trouble with the
>> "datatype" use. If I understand it correctly, the type you map to a
>> "type class" is not the type itself but its datatype (which can be the
>> same).
> That is correct. Hana also calls them generalized types, because
> they are like types, but ones that don't care about the actual
> representation (memory layout) of the objects. In a sense, a data
> type is like a C++ concept, but one that would only be modeled by
> a couple of types that would be exactly the same up to
> representation differences.

I see much more you datatype as a tag.
Do you have an equivalence in Haskell to your datatype/generalized type?
>> In Haskell, type classes can be universally quantified or not. E.g. the
>> "type class" Eq is not universally quantified
>> [...]
>> That means that the instance mapping for Eq is from types (*) and the
>> instance mapping for Functor is for type constructors having one
>> parameter (* ->*).
>> [...]
>> I don't see this nuance in your library. [...]
> This is because this nuance is _voluntarily_ left aside. More
> generally, the design choice I made was to leave parametric
> data types to the documentation level only, as is documented
> in the section about generalized data types[1].
I can understand that you want to let the constraint as comments or
documentation as the Standard library does. However I expect that
implementations do a check at compile time when possible.
> As I already
> said on this list during the last informal review, what I make
> is a tradeoff between mathematical correctness and usability
> of the library. While Hana provides structures that satisfy
> the laws rigorously, I want users to be able to use those
> structures in a quick and dirty way as a mean of being more
> productive when needed. For example, I would not object to
> someone writing [pseudocode]:
> auto to_string = [](auto x) { /* convert x to a std::string */ };
> auto xs = hana::make_tuple(1, 2.2, "abc");
> auto ys = hana::transform(xs, to_string);
> Since `1`, `2.2` and `"abc"` do not share a common data type and
> the signature of `transform` is (for a Functor F)
> transform : F(T) x (T -> U) -> F(U)
> , the above should normally be ill-formed. However, the library
> makes such usage possible because I kept in mind that we're
> working in a "dirty" heterogeneously-typed context. Bottom line:
I don't think heterogeneous types are dirty. We need just a different
type of function (Overloaded) to transform them.

if you have an heterogeneous type as pair for example, the mathematical
transform function should

     transform : pair(T,U) x (T -> R) x (U -> S) -> pair(R, S)

As C++ has overloaded functions we don't need to pass two functions, but
just an overloaded function.
> Concepts in Hana are rigorously specified and coherent internally.
> You are free to bypass them when you need to, because, while it
> would always be possible to do it, it would be painful to always
> justify how operations are mathematically correct [2].
>> Compile-time error reporting
>> --------------------
>> I would expect that the library provide some king of "TypeClass" check
>> use (As Eric's Range library does with Concepts) so that the compile
>> time errors are more explicit.
> Doing this for all parameters of all functions is intractable
> due to the heterogeneous context, but basic support could
> be added easily. I'm promoting this on my todo list.
>> Unnamed data types
>> -----------------
>> I'm missing, between other, the concrete types _either<A,B> and
>> _maybe<T>, as we have _pair<A,B>. How the user can declare a structure
>> having data members with these data types?
> You can't if you don't know beforehand whether it's going to be
> a `just` or a `nothing`. Hence, it is useless to create such
> a data member _explicitly_. The reason is that whether a Maybe
> is a `just` or a `nothing` is encoded in its type, so you
> have to know it if you want to _explicitly_ declare such a
> data member. That's the downside. The upside is that it allows
> you to interact with heterogeneous objects, which was the
> initial goal. There's an example of how this can be used to
> encode SFINAE-failures at [3].
How the fact to have either<A,B> and maybe<A> makes it more dificult to
interact with heterogeneous objects?
>> About a pure type meta programming sub-library Hana/Meta
>> ----------------------------------------------------
>> While I agree that it is good to be able to define the algorithms only
>> once for types and values, I find the syntax cumbersome when the user
>> wants only to work at the meta-programming level.
> I think you overestimate how often you actually need to do
> computations on types. We were just accustomed by the MPL
> to write everything with raw types, but this is not the
> only way to do it. The only times we actually need to
> manipulate types is when we use <type_traits>, which
> does not represent the largest part of metaprogramming
> in my experience.
I have done a lot of meta-programming. Having the simpler syntax would
be much appreciated.
>> I wonder if the library shouldn't contain a sublibrary Hana/Meta that
>> defines in a meta namespace all the algorithms/types that work directly
>> on types and integral constants.
>> Instead of defining a hana::tuple_t, I suggest to define it in
>> meta::tuple that works only with types and a meta::transform that works
>> only on types.
>> This will allow to write ([1])
>> static_assert(
>> meta::transform<meta:: tuple<int, char const, void>,
>> std::add_pointer>{} ==
>> meta::tuple<int*, char const*, void*>{}
>> , "");
>> instead of
>> static_assert(
>> hana::transform(hana::tuple_t<int, char const, void>,
>> hana::metafunction<std::add_pointer>) ==
>> hana::tuple_t<int*, char const*, void*>
>> , "");
>> The definition of meta::transform will just do the steps 2 and 3 of the
>> process you describe:
>> 1. Wrap the types with |type<...>| so they become values
>> 2. Apply whatever type transformation |F| by using |metafunction<F>|
>> 3. Unwrap the result with |decltype(...)::type|
>> [...]
> First of all, the first alternative will almost surely need to
> look more like:
> static_assert(
> meta::transform<meta::tuple<int, char const, void>,
> meta::quote<std::add_pointer>>{} ==
> meta::tuple<int*, char const*, void*>{}
> , "");
> Notice meta::quote.
I wonder if the meta::transform couldn't take care of quote.

> Now, if you compare with
> static_assert(
> hana::transform(hana::tuple_t<int, char const, void>,
> hana::metafunction<std::add_pointer>) ==
> hana::tuple_t<int*, char const*, void*>
> , "");
> , I think you have to admit the difference is tiny. Hana also
> provides integration with <type_traits>, which means that you
> can actually write:
> static_assert(
> hana::transform(hana::tuple_t<int, char const, void>,
> hana::traits::add_pointer) ==
> hana::tuple_t<int*, char const*, void*>
> , "");
Agree, quoted traits are good syntactic sugar
> The difference is that with Hana, you would then need to use
> `decltype(...)::type` to get back the actual type. Again, my
> answer is that this is only required at some thin boundaries
> where you actually need the types.
> HOWEVER, there seem to be some demand for this from the
> community. I'll be very honest and direct with you and
> everyone else on this list: if this addition can make
> people feel more at home because it resembles the MPL,
> and if in turn that eases the process of making Hana
> part of Boost, then I could do it. However, on my part,
> I would find that it encourages people to leverage only
> the _tiny_ part of Hana that deals with type-level
> metaprogramming, and hence reduce what can be done
> with it to a small portion of its full power.
> Also, if I could be shown examples in which the 3-step
> process described above is really painful, and if those
> examples turned out not to simply be non-idiomatic Hana,
> I would add those facilities by the end of the day.
> On a more serious note, and because this issue has to be
> closed once and for all, I created a poll at [4] to resolve
> whether such facilities should be added. It is not a guarantee
> that I'll do it if people want it, but I'd like to have an idea
> of how badly people want it.
Good idea.
>> An additional advantage of having this Meta library is that it can be
>> defined using just a C++11 compiler, as it is done in Eric's Meta library.
> I understand they _could_ be implemented in C++11 only,
> but concretely they would use Hana as a backend and so
> they would need C++14. I would not reimplement those
> operations from scratch.
> Thanks for your comments and questions that never fail
> to challenge me, Vicente.
You are welcome.
> Regards,
> Louis
> [1]:
> [2]: For the example I gave, it would be possible, for example, to
> say that we're creating a Tuple of things that can be converted to
> a `std::string`. Let's call this data type `Stringizable`. Then,
> the data type of the tuple would be `Tuple(Stringizable)`, and
> the signature of transform that we would be using is
> transform : F(Stringizable) x (Stringizable -> std::string) -> F(std::string)
> Until I have a deeper formal understanding of the way this process
> of "intersecting the concepts of the objects in a sequence" works,
> which I'm working on as my bachelor's thesis, I'd rather keep Hana's
> concepts free of higher-order data types. Even if/when I'll have a
> deeper formal understanding, I would likely need language support
> to make this applicable.
My point was more on how the library is checking the fist argument of
Transform is a Functor and that the second argument is a "function" from
the Functor's underlying type T to another type U.

transform : Functor<T> x (T -> U) -> Functor(U)

If the implementation declares transform as

auto transform = [] (auto F, auto Fun)

Couldn't the the library check that the arguments passed in F and Fun respect the requirements?

> [3]:
> [4]:

Boost list run by bdawes at, gregod at, cpdaniel at, john at