Code Monkey home page Code Monkey logo

hana's Issues

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.

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.

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)

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.

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.

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?

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.

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.

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.

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

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?

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.

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.

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);
}

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.

[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&) { }

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.

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.

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.

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?

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;

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.

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.

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.

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

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.

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.

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.