diff --git a/example/list/traversable/sequence.cpp b/example/list/traversable/sequence.cpp new file mode 100644 index 000000000..1503400ea --- /dev/null +++ b/example/list/traversable/sequence.cpp @@ -0,0 +1,33 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +using namespace boost::hana; +using namespace std::string_literals; + + +int main() { + //! [main] + assert( + sequence( + list(list("a1"s, "a2"s), list("b1"s), list("c1", "c2", "c3")) + ) + == + list( + list("a1"s, "b1"s, "c1"s), + list("a1"s, "b1"s, "c2"s), + list("a1"s, "b1"s, "c3"s), + + list("a2"s, "b1"s, "c1"s), + list("a2"s, "b1"s, "c2"s), + list("a2"s, "b1"s, "c3"s) + ) + ); + //! [main] +} diff --git a/example/list/traversable/traverse.cpp b/example/list/traversable/traverse.cpp new file mode 100644 index 000000000..db0a0f648 --- /dev/null +++ b/example/list/traversable/traverse.cpp @@ -0,0 +1,35 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +using namespace boost::hana; + + +int main() { + //! [main] + BOOST_HANA_CONSTEXPR_LAMBDA auto half = [](auto x) { + return if_(x % int_<2> == int_<0>, + just(x / int_<2>), + nothing + ); + }; + + BOOST_HANA_STATIC_ASSERT( + traverse(half, list(int_<2>, int_<4>, int_<6>)) + == + just(list(int_<1>, int_<2>, int_<3>)) + ); + + BOOST_HANA_STATIC_ASSERT( + traverse(half, list(int_<2>, int_<3>, int_<6>)) + == + nothing + ); + //! [main] +} diff --git a/example/maybe/monad.cpp b/example/maybe/monad.cpp index 08fcd01eb..37f75f8d4 100644 --- a/example/maybe/monad.cpp +++ b/example/maybe/monad.cpp @@ -4,6 +4,7 @@ Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) */ +#include #include #include using namespace boost::hana; diff --git a/example/maybe/traversable.cpp b/example/maybe/traversable.cpp new file mode 100644 index 000000000..47f65be50 --- /dev/null +++ b/example/maybe/traversable.cpp @@ -0,0 +1,23 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +using namespace boost::hana; + + +int main() { + //! [main] + BOOST_HANA_CONSTEXPR_LAMBDA auto replicate3 = [](auto x) { + return list(x, x, x); + }; + + BOOST_HANA_STATIC_ASSERT(traverse(replicate3, just(1)) == list(just(1), just(1), just(1))); + BOOST_HANA_STATIC_ASSERT(traverse(replicate3, nothing) == list(nothing)); + //! [main] +} diff --git a/include/boost/hana.hpp b/include/boost/hana.hpp index ce40a5743..bcd821b9a 100644 --- a/include/boost/hana.hpp +++ b/include/boost/hana.hpp @@ -46,6 +46,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include #include #include diff --git a/include/boost/hana/detail/minimal/traversable.hpp b/include/boost/hana/detail/minimal/traversable.hpp new file mode 100644 index 000000000..833051393 --- /dev/null +++ b/include/boost/hana/detail/minimal/traversable.hpp @@ -0,0 +1,63 @@ +/*! +@file +Defines `boost::hana::detail::minimal::Traversable`. + +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_DETAIL_MINIMAL_TRAVERSABLE_HPP +#define BOOST_HANA_DETAIL_MINIMAL_TRAVERSABLE_HPP + +#include +#include +#include + + +namespace boost { namespace hana { +namespace detail { namespace minimal { + template + struct Traversable { }; + + template + struct traversable_type { + X value; + using hana_datatype = Traversable; + }; + + template + struct make_traversable_impl { + template + constexpr auto operator()(X x) const + { return traversable_type{x}; } + }; + + template + constexpr make_traversable_impl traversable{}; +}} // end namespace detail::minimal + +// Provided for convenience only. +template +struct Comparable::instance< + detail::minimal::Traversable, detail::minimal::Traversable +> : Comparable::equal_mcd { + template + static constexpr auto equal_impl(T1 t1, T2 t2) + { return equal(t1.value, t2.value); } +}; + +template <> +struct Traversable::instance> + : Traversable::traverse_mcd +{ + template + static constexpr auto traverse_impl(F f, T trav) { + auto traversable = detail::minimal::traversable; + // this is equivalent to `fmap(traversable, f(trav.value))` + return ap(lift(traversable), f(trav.value)); + } +}; +}} // end namespace boost::hana + +#endif // !BOOST_HANA_DETAIL_MINIMAL_TRAVERSABLE_HPP diff --git a/include/boost/hana/detail/sandbox/traversable.hpp b/include/boost/hana/detail/sandbox/traversable.hpp deleted file mode 100644 index 845617133..000000000 --- a/include/boost/hana/detail/sandbox/traversable.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/*! -@file -Defines `boost::hana::Traversable`. - -@copyright Louis Dionne 2014 -Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - */ - -#ifndef BOOST_HANA_TRAVERSABLE_HPP -#define BOOST_HANA_TRAVERSABLE_HPP - -#include -#include - - -namespace boost { namespace hana { - /*! - @ingroup typeclasses - `Traversable` represents types that can be folded in a - structure-preserving manner. - - -------------------------------------------------------------------------- - - ## Laws - - */ - struct Traversable { - BOOST_HANA_TYPECLASS(Traversable); - struct traverse_mcd; - }; - - //! Structure-preserving left-fold with an `Applicative`. - //! @method{Traversable} - //! - //! ### Example - //! @todo - BOOST_HANA_CONSTEXPR_LAMBDA auto traverse = [](auto f, auto traversable) { - return Traversable::instance< - datatype_t - >::traverse_impl(f, traversable); - }; - - //! Minimal complete definition: `traverse` - struct Traversable::traverse_mcd { - - }; - - template <> - struct Traversable::instance : Traversable::traverse_mcd { - template class A, typename Y> - static constexpr auto traverse_impl(Function> f, Maybe mx) { - return maybe( - lift(nothing), - [](auto x) { return ap(lift(just), f(x)); }, - mx - ); - } - - template - static constexpr auto traverse_impl(F f, M m) { - using A = datatype_t; - nothing -> lift(nothing) - just(x) -> ap(lift(just), f(x)) - } - }; - - template <> - struct Traversable::instance : Traversable::traverse_mcd { - template class A, typename Y> - static constexpr auto traverse_impl(Function> f, List xs) { - auto cons_f = [=](auto x, auto ys) { - return ap(ap(lift(cons), f(x)), ys); - }; - return foldr(cons_f, lift(list()), xs); - } - - template - static constexpr auto traverse_impl(F f, Xs xs) { - using A = ???; - auto cons_f = [=](auto x, auto ys) { - return ap(ap(lift(cons), f(x)), ys); - }; - return foldr(cons_f, lift(list()), xs); - } - }; -}} // end namespace boost::hana - -#endif // !BOOST_HANA_TRAVERSABLE_HPP diff --git a/include/boost/hana/list.hpp b/include/boost/hana/list.hpp index 9c367c5e6..8b69b69df 100644 --- a/include/boost/hana/list.hpp +++ b/include/boost/hana/list.hpp @@ -29,6 +29,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include #include // for std::is_same @@ -45,7 +46,8 @@ namespace boost { namespace hana { -------------------------------------------------------------------------- ## Instance of (as a data type) - `Iterable`, `Functor`, `Applicative`, `Monad`, `Foldable` and `Comparable` + `Iterable`, `Functor`, `Applicative`, `Monad`, `Foldable`, `Traversable`, + and `Comparable` -------------------------------------------------------------------------- @@ -539,6 +541,30 @@ namespace boost { namespace hana { { return foldl(concat, nil, xss); } }; + //! @details + //! This instance is hard to describe in words; see the examples. + //! + //! ### Example + //! @snippet example/list/traversable/traverse.cpp main + //! + //! ### Example + //! @snippet example/list/traversable/sequence.cpp main + template + struct Traversable::instance()>> + : Traversable::traverse_mcd + { + template + static constexpr auto traverse_impl(F f, Xs xs) { + auto curried_cons = [](auto x) { + return [=](auto xs) { return cons(x, xs); }; + }; + auto cons_f = [=](auto x, auto ys) { + return ap(fmap(curried_cons, f(x)), ys); + }; + return foldr(cons_f, lift(nil), xs); + } + }; + //! @details //! Two `List`s are equal if and only if they contain the same number //! of elements and their elements at any given index are equal. diff --git a/include/boost/hana/maybe.hpp b/include/boost/hana/maybe.hpp index 43f243c71..e4879efdc 100644 --- a/include/boost/hana/maybe.hpp +++ b/include/boost/hana/maybe.hpp @@ -18,6 +18,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include namespace boost { namespace hana { @@ -117,6 +118,25 @@ namespace boost { namespace hana { return maybe(s, go, m); } }; + + //! @details + //! Traversing `nothing` yields `nothing` in the new applicative, and + //! traversing `just(x)` applies the function and maps `just` inside + //! the resulting applicative. + //! + //! ### Example + //! @snippet example/maybe/traversable.cpp main + template <> + struct Traversable::instance : Traversable::traverse_mcd { + template + static constexpr auto traverse_impl(F f, M mx) { + return maybe( + lift(nothing), + [=](auto x) { return fmap(just, f(x)); }, + mx + ); + } + }; }} // end namespace boost::hana #endif // !BOOST_HANA_MAYBE_HPP diff --git a/include/boost/hana/traversable.hpp b/include/boost/hana/traversable.hpp new file mode 100644 index 000000000..54fe92439 --- /dev/null +++ b/include/boost/hana/traversable.hpp @@ -0,0 +1,78 @@ +/*! +@file +Defines `boost::hana::Traversable`. + +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_TRAVERSABLE_HPP +#define BOOST_HANA_TRAVERSABLE_HPP + +#include +#include + + +namespace boost { namespace hana { + //! @ingroup typeclasses + //! Data structures that can be traversed from left to right. + struct Traversable { + BOOST_HANA_TYPECLASS(Traversable); + struct traverse_mcd; + }; + + namespace traversable_detail { + template + struct traverse { + template + constexpr auto operator()(F f, T traversable) const { + return Traversable::instance< + datatype_t + >::template traverse_impl(f, traversable); + } + }; + + template + struct sequence { + template + constexpr auto operator()(T traversable) const { + return Traversable::instance< + datatype_t + >::template sequence_impl(traversable); + } + }; + } + + //! Map each element of a structure to an action, evaluate these actions + //! from left to right, and collect the results. + //! @method{Traversable} + //! + //! ### Example + //! @snippet example/list/traversable/traverse.cpp main + //! + //! @note + //! We must specify the target `Applicative` explicitly because the current + //! data-type system is not powerful enough to express the return type of + //! the function passed to `traverse`. + template + constexpr traversable_detail::traverse traverse{}; + + //! Evaluate each action in the structure from left to right, and collect + //! the results. + //! @method{Traversable} + //! + //! ### Example + //! @snippet example/list/traversable/sequence.cpp main + template + constexpr traversable_detail::sequence sequence{}; + + //! Minimal complete definition: `traverse` + struct Traversable::traverse_mcd { + template + static constexpr auto sequence_impl(T traversable) + { return traverse([](auto x) { return x; }, traversable); } + }; +}} // end namespace boost::hana + +#endif // !BOOST_HANA_TRAVERSABLE_HPP diff --git a/test/list/typeclass/traversable/sequence.cpp b/test/list/typeclass/traversable/sequence.cpp new file mode 100644 index 000000000..d21f30d26 --- /dev/null +++ b/test/list/typeclass/traversable/sequence.cpp @@ -0,0 +1,30 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +#include + +#include "../minimal.hpp" +using namespace boost::hana; + + +constexpr auto applicative = detail::minimal::applicative<>; + +template +constexpr auto x = detail::minimal::comparable<>(i); + +int main() { + using A = detail::minimal::Applicative<>; + BOOST_HANA_CONSTEXPR_LAMBDA auto list = minimal_list; + BOOST_HANA_STATIC_ASSERT(sequence(list()) == applicative(list())); + BOOST_HANA_STATIC_ASSERT(sequence(list(applicative(x<0>))) == applicative(list(x<0>))); + BOOST_HANA_STATIC_ASSERT(sequence(list(applicative(x<0>), applicative(x<1>))) == applicative(list(x<0>, x<1>))); + BOOST_HANA_STATIC_ASSERT(sequence(list(applicative(x<0>), applicative(x<1>), applicative(x<2>))) == applicative(list(x<0>, x<1>, x<2>))); +} diff --git a/test/list/typeclass/traversable/traverse.cpp b/test/list/typeclass/traversable/traverse.cpp new file mode 100644 index 000000000..ad7ef359f --- /dev/null +++ b/test/list/typeclass/traversable/traverse.cpp @@ -0,0 +1,40 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +#include + +#include "../minimal.hpp" +#include +using namespace boost::hana; + + +constexpr auto applicative = detail::minimal::applicative<>; + +BOOST_HANA_CONSTEXPR_LAMBDA auto g = [](auto x) { + return std::make_tuple(x); +}; + +BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) { + return applicative(g(x)); +}; + +template +constexpr auto x = detail::minimal::comparable<>(i); + +int main() { + using A = detail::minimal::Applicative<>; + BOOST_HANA_CONSTEXPR_LAMBDA auto list = minimal_list; + BOOST_HANA_STATIC_ASSERT(traverse(f, list()) == applicative(list())); + BOOST_HANA_STATIC_ASSERT(traverse(f, list(x<0>)) == applicative(list(g(x<0>)))); + BOOST_HANA_STATIC_ASSERT(traverse(f, list(x<0>, x<1>)) == applicative(list(g(x<0>), g(x<1>)))); + BOOST_HANA_STATIC_ASSERT(traverse(f, list(x<0>, x<1>, x<2>)) == applicative(list(g(x<0>), g(x<1>), g(x<2>)))); + BOOST_HANA_STATIC_ASSERT(traverse(f, list(x<0>, x<1>, x<2>, x<3>)) == applicative(list(g(x<0>), g(x<1>), g(x<2>), g(x<3>)))); +} diff --git a/test/maybe/traversable/traverse.cpp b/test/maybe/traversable/traverse.cpp new file mode 100644 index 000000000..27896c07f --- /dev/null +++ b/test/maybe/traversable/traverse.cpp @@ -0,0 +1,35 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +#include + +#include +using namespace boost::hana; + + +constexpr auto applicative = detail::minimal::applicative<>; + +BOOST_HANA_CONSTEXPR_LAMBDA auto g = [](auto x) { + return std::make_tuple(x); +}; + +BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) { + return applicative(g(x)); +}; + +template +constexpr auto x = detail::minimal::comparable<>(i); + +int main() { + using A = detail::minimal::Applicative<>; + BOOST_HANA_STATIC_ASSERT(traverse(f, just(x<0>)) == applicative(just(g(x<0>)))); + BOOST_HANA_STATIC_ASSERT(traverse(f, nothing) == applicative(nothing)); +} diff --git a/test/traversable/sequence.cpp b/test/traversable/sequence.cpp new file mode 100644 index 000000000..ec88acc50 --- /dev/null +++ b/test/traversable/sequence.cpp @@ -0,0 +1,31 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +#include +using namespace boost::hana; + + +template +void test() { + constexpr auto traversable = detail::minimal::traversable; + constexpr auto applicative = detail::minimal::applicative<>; + constexpr auto comparable = detail::minimal::comparable<>; + + BOOST_HANA_STATIC_ASSERT( + sequence>(traversable(applicative(comparable(1)))) + == + applicative(traversable(comparable(1))) + ); +} + +int main() { + test(); +} diff --git a/test/traversable/traverse.cpp b/test/traversable/traverse.cpp new file mode 100644 index 000000000..3cfbc66ea --- /dev/null +++ b/test/traversable/traverse.cpp @@ -0,0 +1,37 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +#include +#include + +#include +using namespace boost::hana; + + +template +void test() { + constexpr auto traversable = detail::minimal::traversable; + constexpr auto applicative = detail::minimal::applicative<>; + constexpr auto comparable = detail::minimal::comparable<>; + BOOST_HANA_CONSTEXPR_LAMBDA auto f = [=](auto x) { + return applicative(std::make_tuple(x)); + }; + + BOOST_HANA_STATIC_ASSERT( + traverse>(f, traversable(comparable(1))) + == + applicative(traversable(std::make_tuple(comparable(1)))) + ); +} + +int main() { + test(); +}