Code Monkey home page Code Monkey logo

hana's Introduction

Boost.Hana Version Travis status Appveyor status Try it online Gitter Chat

Your standard library for metaprogramming

Overview

#include <boost/hana.hpp>
#include <cassert>
#include <string>
namespace hana = boost::hana;
using namespace hana::literals;

struct Fish { std::string name; };
struct Cat  { std::string name; };
struct Dog  { std::string name; };

int main() {
  // Sequences capable of holding heterogeneous objects, and algorithms
  // to manipulate them.
  auto animals = hana::make_tuple(Fish{"Nemo"}, Cat{"Garfield"}, Dog{"Snoopy"});
  auto names = hana::transform(animals, [](auto a) {
    return a.name;
  });
  assert(hana::reverse(names) == hana::make_tuple("Snoopy", "Garfield", "Nemo"));

  // No compile-time information is lost: even if `animals` can't be a
  // constant expression because it contains strings, its length is constexpr.
  static_assert(hana::length(animals) == 3u, "");

  // Computations on types can be performed with the same syntax as that of
  // normal C++. Believe it or not, everything is done at compile-time.
  auto animal_types = hana::make_tuple(hana::type_c<Fish*>, hana::type_c<Cat&>, hana::type_c<Dog*>);
  auto animal_ptrs = hana::filter(animal_types, [](auto a) {
    return hana::traits::is_pointer(a);
  });
  static_assert(animal_ptrs == hana::make_tuple(hana::type_c<Fish*>, hana::type_c<Dog*>), "");

  // And many other goodies to make your life easier, including:
  // 1. Access to elements in a tuple with a sane syntax.
  static_assert(animal_ptrs[0_c] == hana::type_c<Fish*>, "");
  static_assert(animal_ptrs[1_c] == hana::type_c<Dog*>, "");

  // 2. Unroll loops at compile-time without hassle.
  std::string s;
  hana::int_c<10>.times([&]{ s += "x"; });
  // equivalent to s += "x"; s += "x"; ... s += "x";

  // 3. Easily check whether an expression is valid.
  //    This is usually achieved with complex SFINAE-based tricks.
  auto has_name = hana::is_valid([](auto&& x) -> decltype((void)x.name) { });
  static_assert(has_name(animals[0_c]), "");
  static_assert(!has_name(1), "");
}

Documentation

You can browse the documentation online at http://boostorg.github.io/hana. The documentation covers everything you should need including installing the library, a tutorial explaining what Hana is and how to use it, and an extensive reference section with examples. The remainder of this README is mostly for people that wish to work on the library itself, not for its users.

An offline copy of the documentation can be obtained by checking out the gh-pages branch. To avoid overwriting the current directory, you can clone the gh-pages branch into a subdirectory like doc/html:

git clone http://github.com/boostorg/hana --branch=gh-pages --depth=1 doc/html

After issuing this, doc/html will contain exactly the same static website that is available online. Note that doc/html is automatically ignored by Git so updating the documentation won't pollute your index.

Hacking on Hana

Setting yourself up to work on Hana is easy. First, you will need an installation of CMake. Once this is done, you can cd to the root of the project and setup the build directory:

mkdir build
cmake -S . -B build

Sometimes, you'll want to specify a custom compiler because the system's compiler is too old:

cmake -S . -B build -DCMAKE_CXX_COMPILER=/path/to/compiler

Usually, this will work just fine. However, on some older systems, the standard library and/or compiler provided by default does not support C++14. If this is your case, the wiki has more information about setting you up on different systems.

Normally, Hana tries to find Boost headers if you have them on your system. It's also fine if you don't have them; a few tests requiring the Boost headers will be disabled in that case. However, if you'd like Hana to use a custom installation of Boost, you can specify the path to this custom installation:

cmake -S . -B build -DCMAKE_CXX_COMPILER=/path/to/compiler -DBOOST_ROOT=/path/to/boost

You can now build and run the unit tests and the examples:

cmake --build build --target check

You should be aware that compiling the unit tests is pretty time and RAM consuming, especially the tests for external adapters. This is due to the fact that Hana's unit tests are very thorough, and also that heterogeneous sequences in other libraries tend to have horrible compile-time performance.

There are also optional targets which are enabled only when the required software is available on your computer. For example, generating the documentation requires Doxygen to be installed. An informative message will be printed during the CMake generation step whenever an optional target is disabled. You can install any missing software and then re-run the CMake generation to update the list of available targets.

Tip

You can use the help target to get a list of all the available targets.

If you want to add unit tests or examples, just add a source file in test/ or example/ and then re-run the CMake generation step so the new source file is known to the build system. Let's suppose the relative path from the root of the project to the new source file is path/to/file.cpp. When you re-run the CMake generation step, a new target named path.to.file will be created, and a test of the same name will also be created. Hence,

cmake --build build --target path.to.file # Builds the program associated to path/to/file.cpp
ctest --test-dir build -R path.to.file # Runs the program as a test

Tip for Sublime Text users

If you use the provided hana.sublime-project file, you can select the "[Hana] Build current file" build system. When viewing a file to which a target is associated (like a test or an example), you can then compile it by pressing ⌘B, or compile and then run it using ⇧⌘B.

Project organization

The project is organized in a couple of subdirectories.

  • The benchmark directory contains compile-time and runtime benchmarks to make sure the library is as fast as advertised. The benchmark code is written mostly in the form of eRuby templates. The templates are used to generate C++ files which are then compiled while gathering compilation and execution statistics.
  • The cmake directory contains various CMake modules and other scripts needed by the build system.
  • The doc directory contains configuration files needed to generate the documentation. The doc/html subdirectory is automatically ignored by Git; you can conveniently store a local copy of the documentation by cloning the gh-pages branch into that directory, as explained above.
  • The example directory contains the source code for all the examples of both the tutorial and the reference documentation.
  • The include directory contains the library itself, which is header only.
  • The test directory contains the source code for all the unit tests.

Contributing

Please see CONTRIBUTING.md.

License

Please see LICENSE.md.

Releasing

Releasing is now done exclusively via the Boost release process. There are no separate releases of Hana since the library is now pretty stable.

hana's People

Contributors

badair avatar bebuch avatar dvd0101 avatar ecatmur avatar eldiener avatar grafikrobot avatar johelegp avatar kholdstare avatar kojoley avatar ldionne avatar m-j-w avatar marcelraad avatar marisakirisame avatar nathanielhourt avatar pdimov avatar qchateau avatar ricejasonf avatar sbolding-lanl avatar sdebionne avatar shreyans800755 avatar snizovtsev avatar tinko92 avatar tzlaine avatar vittorioromeo avatar xiangfan-ms avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hana's Issues

Poll: Should Hana provide MPL-like metafunctions for type-only computations?

As expressed on the Boost mailing list, there seems to be some interest from the community in having a meta namespace in which a type-level interface (like MPL and <type_traits>) for a subset of the library is provided. I am reluctant to add it because I see it as encouraging non-idiomatic Hana, but I want to know how badly people want it. Please write a comment saying whether you would like that, and ideally why.

For an example of what's being proposed, we would be able to write

static_assert(
    meta::transform<meta::tuple<int, char const, void>,
        meta::quote<std::add_pointer>>{} ==
    meta::tuple<int*, char const*, void*>{}
 , "");

using Xs = meta::transform<
    meta::tuple<int, char const, void>, 
    meta::quote<std::add_pointer>
>;

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*>
, "");

using Xs = decltype(hana::transform(
    hana::tuple_t<int, char const, void>, 
    hana::metafunction<std::add_pointer>
))::type;

Add missing functions provided by MPL/Fusion

Some functions that might be useful and that are currently missing from Hana are:

  • erase(sequence, index)
  • erase(sequence, from, to)
  • insert_range(sequence, index, range)
  • erase_key(set-or-map, key)
  • unique, unique_by

Another function that is missing from Hana but not in MPL/Fusion is split_at(index, sequence), which should return a pair of two sequences (before, after).

Consider allowing custom key comparators in associative data structures

Right now, a Set uses equal on its elements to determine their uniqueness. It might be useful to provide a custom key comparator for more complex behaviours. In the same vein, Map currently uses equal on its keys to determine the uniqueness of elements; it might be useful to allow for a custom comparator.

However, compile-time sets are super hairy to implement, and honestly I am unsure how this could be achieved without ruining the compile-time performance of the associative data structures (which is already super poor because the implementation is naive). Also note that I do not think a compile-time efficient associative data structure can be implemented unless we put stringent requirements on how the elements are compared. Specifically, the only compile-time efficient associative data structure I can think of is where all the keys are types, or all the keys are indices (in which case it's a tuple).

Consider eliminating all partial functions

Would it be desirable to eliminate partial functions, i.e. functions that fail for some inputs in their domain? For example, head can fail when given an empty Sequence. Instead of having head return an element or fail if the sequence is empty, it could be specified as returning a Maybe containing the element or nothing. This makes the function "safe", but it's also slightly more cumbersome to use.

Another possibility is to provide two versions of head; one which is safe and returns a Maybe, and one which isn't and may fail. It could be called something like head and head.safe.

I'm putting this issue here mostly to get it out of my head (and huge list of local issues I maintain locally), but I've grown less and less in favour of it. If someone has an opinion about this, please comment.

Review the Constantness of comparing Pairs

The following comparison does not return a Constant:

#include <boost/hana/pair.hpp>

struct invalid { };

int main() {
    hana::make_pair('x', invalid{}) == hana::make_pair('x', 2);
}

One could argue that it should, because invalid{} == 2 returns false_. Fixing this for Pair should be fairly easy, but it raises the issue that Tuple should do the same, which is probably harder to implement.

Provide a model of Foldable for Map

I initially thought this would violate the consistency of the model for Searchable, but it does not because that consistency is only required for finite Iterables, and Map is obviously not Iterable.

Should enumerations be Enumerable?

This was triggered a while ago by a discussion on the Boost.Devel mailing list. We could provide a macro allowing an enumeration to be defined, while at the same time giving it a model of the Enumerable concept. I'll consider adding this if enough people want it.

slice(xs, from, to) vs slice(xs, from, length)

Currently, slice takes a start and a one-past-the-end index. Instead, it could take a start index and a length. I'm not sure which one is better. So the question is whether slice(xs, from, to) or slice(xs, from, length) should be preferred.

Benchmark code bloat

We should document and measure the size of various executables to check what's the penalty for using Hana in terms of code bloat.

Perfect forwarding breaks interoperation with `type<>`

Since we started adding perfect forwarding all around the place, some methods now return references instead of plain values. For example,

auto types = tuple(type<T>, type<U>, type<V>);
static_assert(std::is_same<
    decltype(head(types)),
    decltype(type<T>)&
>::value, "");

This causes stuff like decltype(head(types))::type to fail, which is a huge problem.

Reformulate the Sequence laws

The current Sequence laws (as introduced in 55e4a1d) are pretty bold. The relation between being the "unique natural isomorphism between Tuple and S" and being "equivalent to Tuple" is really not as clear as I make it out. Actually, this is probably false if we are not more precise.

The laws as currently written will at least convey the intuition that Sequences are the same as Tuples, which is currently true. However, another question is whether we actually want this, since it precludes any runtime or homogeneous sequence from modelling that concept.

Consider renaming hana::demux and hana::lockstep

I have never been really fond of those, but I could never think of something better. If someone can come up with something that's better, I'll be happy to change it. To understand what those guys do, see here for demux and here for lockstep. Basically, they are two different ways to compose functions, along with compose.

MPL related errors while building tests and examples

Hi!

I was trying to build the tests and the examples and I came across these errors and I cannot figure out whether it is because of my version of boost libs or something else causes them.

I am on Ubuntu 14.04 x64, with clang 3.7 and libcxx (both) trunk - updated yesterday, boost trunk as well. Therefore, the only explanation I find for these errors is that you may have an unofficial/patched version of boost headers.

Firstly, in example/tutorial/type_computations.cpp, the following piece of code does not compile:

static_assert(mpl::equal<                                                        
       apply_to_all<types, mpl::quote1<std::add_pointer>>::type,                    
       mpl::vector<                                                                 
           mpl::vector<int*, char*>,                                                
           mpl::vector<void*, std::string*, double*>                                
       >,                                                                           

       // mpl::equal was seemingly not designed for deep comparisons, so we         
       // need this tricky line for it to do what we want.                          
       mpl::equal<mpl::_1, mpl::_2, mpl::quote2<std::is_same>>                      
   >{}, "");

Replacing the instantiation>{} with >::type::value makes it compile.

Secondly, I am getting the following errors [0] [1] when further building the tests and the examples (the output is too lengthy).

What am I missing? Thanks!

P.S.: Sorry if it is some mistake from my side, but I really want to compile them successfully.

Poll: Let's find a name for foldl and foldr

It was suggested on the Boost mailing list that foldl and foldr were ugly. Let's settle this with a poll. Leave a comment to vote for an option or to propose a new option.

Since there are also foldl1 and foldr1, which are equivalent to foldl and foldr but do not take an initial state, it seems legitimate to ask that a valid naming convention also accounts for them. Here are the current propositions:

  1. foldl/foldr/foldl1/foldr1 (as is)
  2. fold/reverse_fold/??/??
  3. fold_left/fold_right/??/??
  4. fold.left/fold.right/??/?? (optionally, we could have fold == fold.left)

[improvement] not possible to pass hana::type_list as function parameter?

its not really hana issue, but would be nice to have.

since type_list is a variable template returning tlist_detail::type_list<xs...>::type is not possible to pass it to function?

guess, it would works if not for nested type struct?

    namespace tlist_detail {
        template <typename ...xs>
        struct type_list {
            struct type : operators::enable {
                using hana_datatype = TypeList;

                template <template <typename ...> class f>
                using storage = f<xs...>;

                template <typename F>
                static constexpr auto storage_(F f) {
                    return f.template apply<xs...>();
                }
            };
        };
    }
template<typename... TArgs>
void f(const typename hana::tlist_detail::type_list<TArgs...>::type&) { }

int main() {
    f(hana::type_list<int, double>);
}

would be great to be able to do sth like that:

template<typename... TArgs>
void f(const hana::type_list<TArgs...>&) { }

error: no matching function for call to 'f'
note: candidate function [with TArgs = <>] not viable: no known conversion from 'const typename tlist_detail::type_list<int, double>::type' to 'const typename hana::tlist_detail::type_list<>::type'
for 1st argument
void f(const typename hana::tlist_detail::type_list<TArgs...>::type&) { }

Support mixed data types in zip_with

Currently, it only makes sense to use zip_with with objects that all have the same data type, since only the data type of the first object is used for dispatching. It might make sense to allow objects of different data types, but I'm not sure how that could be implemented. Questions that have to be answered and possible issues include:

  • Would it require multiple dispatch at the data type level?
  • Would it actually be useful? How could one implement a zip_with not knowing the data types of the objects?

Rename Monad::bind

The name is misleading because of the existence of std::bind. If anyone can come up with a better name, please suggest it.

has_operator should return an IntegralConstant

More generally, everything in core should use the same consistent interface. Unfortunately, this raises issues of circular dependencies. Or perhaps core should be minimal and not use IntegralConstants at all?

Test in release mode

When compiling in Release mode (DCMAKE_BUILD_TYPE=Release) CMake passes the flag -DNDEBUG which disables assert(...). The problem is that assert is used by e.g. BOOST_HANA_RUNTIME_ASSERT so these checks are not performed in Release mode. Maybe a different macro to perform runtime checks during the tests could be provided.

Support runtime Iterables

Sequences whose constructor(s) are not constexpr can't always know their bounds at compile-time. What we would like is for Iterable and such concepts to provide defaults that also work for these runtime sequences. The difference between runtime sequences and compile-time sequences is that the is_empty method can only return a runtime bool for the former, while it returns an IntegralConstant for the latter. Actually, we're talking about compile-time and runtime Logicals, but the difference is thin.

This issue presents the current state of things concerning support for runtime Iterables.

Below is a list of attempts I made, when the library first started, to get the algorithms to work for both runtime and compile-time Iterables. This list is kept mostly as an archive. Since then, I think I have found a way of doing it by encapsulating recursion in the while_ construct. However, more research is needed to confirm this and to see how/if it would all fit together in the library.

Attempt 1

This works, but it requires a lot of repetition:

template <typename Index, typename Iterable_>
static constexpr auto at_helper(Bool<true>, Index n, Iterable_ iterable)
{ return head(iterable); }

template <typename Index, typename Iterable_>
static constexpr auto at_helper(Bool<false>, Index n, Iterable_ iterable)
{ return at_helper(n == size_t<1>, n - size_t<1>, tail(iterable)); }

template <typename Index, typename Iterable_>
static constexpr auto at_helper(bool cond, Index n, Iterable_ iterable) {
    if (cond) return head(iterable);
    else      return at_helper(n == size_t<1>, n - size_t<1>, tail(iterable));
}

template <typename Index, typename Iterable_>
static constexpr auto at_impl(Index n, Iterable_ iterable)
{ return at_helper(n == size_t<0>, n, iterable); }

Attempt 2

The obvious improvement is to use an external if_ that factors the repetition away. Unfortunately, it does not work.

constexpr struct _lazy_if {
    template <typename Then, typename Else, typename ...Args>
    constexpr auto operator()(Bool<true>, Then t, Else, Args ...args) const
    { return t(args...); }

    template <typename Then, typename Else, typename ...Args>
    constexpr auto operator()(Bool<false>, Then, Else e, Args ...args) const
    { return e(args...); }

    template <typename Then, typename Else, typename ...Args>
    constexpr auto operator()(bool cond, Then t, Else e, Args ...args) const {
        if (cond) return t(args...);
        else      return e(args...);
    }
} lazy_if{};


template <typename Index, typename Iterable_>
static constexpr auto at_impl(Index n, Iterable_ iterable) {
    return lazy_if(n == size_t<0>,
        [](auto n, auto it) { return head(it); },
        [](auto n, auto it) { return at_impl(n - size_t<1>, tail(it)); },
        n, iterable
    );
}

Note that we pass Args... to the lazy_if because otherwise we would need to return two different lambdas from the same function in the bool case. I think the reason for it not working is because we introduce a new intermediate lambda

[](auto n, auto it) { return at_impl(n - size_t<1>, tail(it)); },

in the then branch, which causes the compiler to hit the instantiation limit while trying to recursively instantiate at_impl when it instantiates the lambda.

Attempt 2.1

Using a different if_, but it still does not work.

constexpr struct _tri_if {
    template <typename Then, typename Else, typename Maybe, typename ...Args>
    constexpr auto operator()(Bool<true> cond, Then t, Else, Maybe, Args ...args) const
    { return t(cond, args...); }

    template <typename Then, typename Else, typename Maybe, typename ...Args>
    constexpr auto operator()(Bool<false> cond, Then, Else e, Maybe, Args ...args) const
    { return e(cond, args...); }

    template <typename Then, typename Else, typename Maybe, typename ...Args>
    constexpr auto operator()(bool cond, Then, Else, Maybe m, Args ...args) const
    { return m(cond, args...); }
} tri_if{};

template <typename Index, typename Iterable_>
static constexpr auto at_impl(Index n, Iterable_ iterable) {
    return tri_if(n == size_t<0>,
        [](auto cond, auto n, auto it) { return head(it); },
        [](auto cond, auto n, auto it) { return at_impl(n - size_t<1>, tail(it)); },
        [](bool cond, Index n, Iterable_ it) {
            return cond ? head(it) : at_impl(n - size_t<1>, tail(it));
        },
        n, iterable
    );
}

Attempt 3

Here, I'm trying to use structs to make everything more explicit. Still does not work because the problem is when instantiating the inner apply function. See the comment below.

template <typename Cond, typename = void>
struct at_helper;

template <typename Dummy>
struct at_helper<Bool<true>, Dummy> {
    template <typename Index, typename Iterable_>
    static constexpr auto apply(Bool<true>, Index n, Iterable_ iterable) {
        return head(iterable);
    }
};

template <typename Dummy>
struct at_helper<Bool<false>, Dummy> {
    template <typename Index, typename Iterable_>
    static constexpr auto apply(Bool<false>, Index n, Iterable_ iterable) {
        return at_helper<decltype(n == size_t<1>)>::apply(
            n == size_t<1>, n - size_t<1>, tail(iterable)
        );
    }
};

template <typename Dummy>
struct at_helper<bool, Dummy> {
    template <typename Index, typename Iterable_>
    static constexpr auto apply(bool cond, Index n, Iterable_ iterable) {
        if (cond)
            return head(iterable);
        else
            // This triggers infinite recursive instantiations.
            // return at_helper<Bool<false>>::apply(false_, n, iterable);

            // This works. decltype(...) is actually bool.
            return at_helper<decltype(n == size_t<1>)>::apply(
                n == size_t<1>, n - size_t<1>, tail(iterable)
            );
    }
};

template <typename Index, typename Iterable_>
static constexpr auto at_impl(Index n, Iterable_ iterable) {
    return at_helper<decltype(n == size_t<0>)>::apply(n == size_t<0>, n, iterable);
}

Attempt 4

It works, but it's not a solution. I basically use the preprocessor to generate some of the duplicate code.

#define HACK(f)                                                             \
template <TPARAMS>                                                          \
static constexpr auto f(Bool<true>, PARAMS)                                 \
{ return TRUE_BRANCH; }                                                     \
                                                                            \
template <TPARAMS>                                                          \
static constexpr auto f(Bool<false>, PARAMS)                                \
{ return FALSE_BRANCH; }                                                    \
                                                                            \
template <TPARAMS>                                                          \
static constexpr auto f(bool cond, PARAMS) {                                \
    if (cond) return TRUE_BRANCH;                                           \
    else      return FALSE_BRANCH;                                          \
}                                                                           \
/**/

#define TPARAMS typename Index, typename Iterable_
#define PARAMS Index n, Iterable_ iterable
#define TRUE_BRANCH head(iterable)
#define FALSE_BRANCH at_helper(n == size_t<1>, n - size_t<1>, tail(iterable))

HACK(at_helper);

template <typename Index, typename Iterable_>
static constexpr auto at_impl(Index n, Iterable_ iterable)
{ return at_helper(n == size_t<0>, n, iterable); }

Attempt 5

Trying to factor out the branches into lambdas. This recursively instantiates stuff too.

BOOST_HANA_CONSTEXPR_LAMBDA auto true_branch = [](auto at_impl, auto n, auto iterable)
{ return head(iterable); };

BOOST_HANA_CONSTEXPR_LAMBDA auto false_branch = [](auto at_impl, auto n, auto iterable)
{ return at_impl(n - size_t<1>, tail(iterable)); };

template <typename F, typename Index, typename Iterable_>
static constexpr auto at_helper(F at_impl, Bool<true>, Index n, Iterable_ iterable)
{ return true_branch(at_impl, n, iterable); }

template <typename F, typename Index, typename Iterable_>
static constexpr auto at_helper(F at_impl, Bool<false>, Index n, Iterable_ iterable)
{ return false_branch(at_impl, n, iterable); }

template <typename F, typename Index, typename Iterable_>
static constexpr auto at_helper(F at_impl, bool cond, Index n, Iterable_ iterable) {
    if (cond) return true_branch(at_impl, n, iterable);
    else      return false_branch(at_impl, n, iterable);
}

BOOST_HANA_CONSTEXPR_LAMBDA auto at_impl = fix(
    [](auto at_impl, auto n, auto iterable)
    { return at_helper(at_impl, n == size_t<0>, n, iterable); }
);

Attempt 6

Make everything explicit by using structs.

template <typename Condition, typename Index, typename Iterable_>
struct at_helper;

template <typename AtHelper>
struct false_branch {
    template <typename Index, typename It>
    static constexpr auto apply(Index n, It it) {
        return AtHelper::apply(n == size_t<1>, n - size_t<1>, tail(it));
    }
};

template <typename Index, typename Iterable_>
struct at_helper<Bool<true>, Index, Iterable_> {
    static constexpr auto apply(Bool<true>, Index n, Iterable_ it)
    { return head(it); }
};

template <typename Index, typename Iterable_>
struct at_helper<Bool<false>, Index, Iterable_> {
    static constexpr auto apply(Bool<false>, Index n, Iterable_ it) {
        // This works
        return false_branch<at_helper<decltype(n == size_t<1>), decltype(n - size_t<1>), decltype(tail(it))>>::apply(n, it);

        // This works too.
        // return at_helper<decltype(n == size_t<1>), decltype(n - size_t<1>), decltype(tail(it))>::
        //        apply(n == size_t<1>, n - size_t<1>, tail(it));
    }
};

template <typename Index, typename Iterable_>
struct at_helper<bool, Index, Iterable_> {
    static constexpr auto apply(bool cond, Index n, Iterable_ it) {
        if (cond) return head(it);
                  // Surprisingly, using the false_branch<...> just like above
                  // does not work here. WTF!
        else      return at_helper<decltype(n == size_t<1>), decltype(n - size_t<1>), decltype(tail(it))>::
                         apply(n == size_t<1>, n - size_t<1>, tail(it));
    }
};

template <typename Index, typename Iterable_>
static constexpr auto at_impl(Index n, Iterable_ iterable) {
    return at_helper<decltype(n == size_t<0>), decltype(n), decltype(iterable)>::
           apply(n == size_t<0>, n, iterable);
}

from_just() returns a wrong value

Here's the snippet:

auto a = maybe('x', compose(id, always), just(18))();
std::cout << a << std::endl;   // 18

auto b = maybe('x', compose(id, always), just(19));
std::cout << b() << std::endl; // 19

auto c = from_just(just(1)); // any integer other than 1 produces the same wrong result
std::cout << c << std::endl;  // 4098!!!

And if the implementation of from_just in fwd/maybe.hpp is changed to:

template <typename M>
constexpr decltype(auto) operator()(M&& m) const {
       return maybe(
             error{}, compose(id, always), detail::std::forward<M>(m)
       );
}

then the result from_just(just(1))() is correct.

hana, clang and llvm are all compliled from the the latest trunk.

Specify the type of some containers

Right now, the actual C++ type of Hana's heterogeneous containers is never specified. For example, tuple(1, '2') is an object whose type is unspecified (and in fact useless because it involves the type of a lambda and other stuff). This is mainly to give room to the implementation. However, it would definitely add value if the type of containers was specified (or at least more specified), so we could pattern match on them and do other useful stuff:

template <typename ...T>
void f(hana::tuple_type<T...> t);

If this is feasible, i.e. if the library can still be implemented while specifying the type of containers, a possible way to achieve this would be:

  • hana::tuple<...> is a type (like std::tuple<...>), and hana::make<hana::Tuple> is equivalent to the current hana::tuple function
  • hana::map<...> is a type, and hana::make<hana::Map> creates one of these.
  • same for hana::pair<>, hana::set<> & al.

Things to consider if we go this way

  • The compile-time performance of containers could be influenced by longer names to mangle. We'd have to benchmark the difference.
  • Some objects just can't have their type fully specified. For example, hana::type<T> has to hide T from ADL, which requires it being a dependent type. In this precise case, what we could specify is that type<T> is of a type which derives from something that's specified. That would still allow us to pattern match in function templates and perhaps other contexts. In general, we would need to document exactly what is defined and what is left undefined about the type of each object.
  • We might end up with naming conflicts, e.g. having a function which should really have the same name as the type of some object. Before we do what's being proposed here, we should have a clear plan of how we're going to rename things so everything stays consistent.

Consider adding a iota function

iota(from, to) could create a Sequence containing the IntegralConstants, or it could also create a Range. Creating a Range would be more compile-time efficient, but maybe that's not what we need (we already have make<Range>). This also opens the question of whether a custom step should be allowed, how to make this compile-time efficient if we return a Sequence, and so on.

clang-3.6 segfault

The following snippet causes clang-3.6 segfault.

#include <boost/hana.hpp>

auto make_string = [](auto key) {
    return BOOST_HANA_STRING(key);
};

int main() {
    make_string("yes");
}

Am I misusing anything?

not possible to return different types from hana::if_ statement?

struct fold_and_add_type_list {
    using type = decltype(
        hana::foldl(
            [](auto result, auto t) {
                return hana::if_(
                    true
                  , hana::snoc(result, t)
                  , result
                );
            }
          , hana::type_list<>
          , hana::type_list<int, double>
        )
    );
};

../include/boost/hana/logical/logical.hpp:215:25: error: incompatible operand types ('boost::hana::tlist_detail::type_list::type' and 'boost::hana::tlist_detail::type_list<>::type')
return cond ? t(id) : e(id);

Minor documentation issue

In the getting concrete section, inside the definition of the typeclass Printable, the following comment:

// Not invalid, but probably stupid

should probably be changed to "Valid, but probably useless" (or similar).

Otherwise the documentation is great, congrats.

examples do not build

../example/foldable.cpp:30:48: error: no member named 'str' in 'std::basic_ostream<char>'
            return (std::ostringstream{} << x).str();

This is with clang 3.6.0 885f51439b619bfc42e47c70bd3404ceedcf259f on Linux.

mcompose only works for unary functions

The way mcompose is currently specified and implemented only works for unary functions. Calling mcompose<...>(f, g) with more than one argument is an error. We should probably do something similar to what's done in compose.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.