boostorg / hana Goto Github PK
View Code? Open in Web Editor NEWYour standard library for metaprogramming
Home Page: http://boostorg.github.io/hana
License: Boost Software License 1.0
Your standard library for metaprogramming
Home Page: http://boostorg.github.io/hana
License: Boost Software License 1.0
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 Sequence
s are the same as Tuple
s, 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.
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);
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.
For Iterables
, Searchable
could use at
instead of recursive iteration whenever that's more efficient, which is probably always the case anyway.
The current documentation uses petty problems to showcase the library. Instead, we could present some more advanced examples.
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:
foldl
/foldr
/foldl1
/foldr1
(as is)fold
/reverse_fold
/??/??fold_left
/fold_right
/??/??fold.left
/fold.right
/??/?? (optionally, we could have fold == fold.left
)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.
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.
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?
We're currently in v0.1 and following the version scheme of Semantic Versioning, but this should be documented and detailed more precisely.
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.
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.
For example, see operators::Iterable_ops
. This is ugly and I want a more general system for providing those.
The name is misleading because of the existence of std::bind
. If anyone can come up with a better name, please suggest it.
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)
.
Do we want char_<1> + char_<2> == char_<3>
or char_<1> + char_<2> == int_<3>
? I think I remember that @jfalcou had raised this issue at C++Now last year, but we never got around to discussing it.
Hana uses Boost without searching for it in its CMake code.
I think it's not supported right now because of this bug. Make sure that's still relevant.
Would that interact well with the rest of the library? Would that be useful? This brings out the more general question of how to deal with parameterized type classes, which is a can of worms. See this for some more comments on parameterized type classes.
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 IntegralConstant
s at all?
I suggest to rename Functor::list_mcd to Functor::when_list.
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.
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
functionhana::map<...>
is a type, and hana::make<hana::Map>
creates one of these.hana::pair<>
, hana::set<>
& al.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.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.
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); }
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.
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
);
}
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);
}
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); }
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); }
);
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);
}
We should document and measure the size of various executables to check what's the penalty for using Hana in terms of code bloat.
IntegralDomain is the wrong name for that concept. In abstract algebra, an IntegralDomain is not enough to provide quot
and rem
. Technically, I think we need an Euclidean Domain.
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&) { }
iota(from, to)
could create a Sequence
containing the IntegralConstant
s, 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.
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
.
This requires having a proper way to handle lazy boolean expressions, which is not exactly trivial.
Given a sequence [x1, ..., xn]
, we need a way to extract a subsequence of the elements at indices [i1, ..., ik]
.
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
.
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:
zip_with
not knowing the data types of the objects?I'm not sure it's possible to do so with an Integral Domain (or an Euclidean ring, see #28). Also consider adding quot_rem
, which might allow optimizations.
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;
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 Iterable
s, and Map
is obviously not Iterable
.
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.
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.
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).
This implementation is more compile-time efficient than the previous one, but it does not allow for infinite Iterables. We could provide this implementation only for Foldables, since Foldables must be finite.
Suggestion to lower the barriers to collaboration on the project.
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.
Right now, we can't sum a tuple of an arbitrary Monoid because zero<IntegralConstant<int>>
is used. It should be possible to specify the Monoid to use. One possibility is to add sum.of<T>
, and to have sum
be equivalent to sum.of<IntegralConstant<int>>
by default.
something that is sort of like boost::mpl::push_front
../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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.