123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- // Copyright Louis Dionne 2013-2017
- // 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 <boost/hana/lazy.hpp>
- #include <boost/hana/ap.hpp>
- #include <boost/hana/assert.hpp>
- #include <boost/hana/chain.hpp>
- #include <boost/hana/concept/comparable.hpp>
- #include <boost/hana/config.hpp>
- #include <boost/hana/duplicate.hpp>
- #include <boost/hana/equal.hpp>
- #include <boost/hana/eval.hpp>
- #include <boost/hana/extend.hpp>
- #include <boost/hana/extract.hpp>
- #include <boost/hana/flatten.hpp>
- #include <boost/hana/functional/compose.hpp>
- #include <boost/hana/lift.hpp>
- #include <boost/hana/transform.hpp>
- #include <boost/hana/tuple.hpp>
- #include <laws/applicative.hpp>
- #include <laws/base.hpp>
- #include <laws/comonad.hpp>
- #include <laws/functor.hpp>
- #include <laws/monad.hpp>
- #include <support/tracked.hpp>
- #include <array>
- #include <iostream>
- namespace hana = boost::hana;
- using hana::test::ct_eq;
- namespace boost { namespace hana {
- // We provide this instance for unit tests only because it is _so_ much
- // more convenient, but this instance is too dangerous for general usage.
- // See the documentation of `hana::lazy` for more info.
- template <>
- struct equal_impl<lazy_tag, lazy_tag> {
- template <typename X, typename Y>
- static constexpr auto apply(X x, Y y)
- { return hana::equal(hana::eval(x), hana::eval(y)); }
- };
- }}
- auto invalid = [](auto x)
- { return x.this_function_must_not_be_instantiated; };
- int main() {
- hana::test::_injection<0> f{};
- auto eqs = hana::make_tuple(
- hana::make_lazy(ct_eq<0>{}),
- hana::make_lazy(ct_eq<1>{}),
- hana::make_lazy(ct_eq<2>{})
- );
- auto eq_elems = hana::make_tuple(ct_eq<0>{}, ct_eq<1>{}, ct_eq<1>{});
- auto nested = hana::make_tuple(
- hana::make_lazy(hana::make_lazy(ct_eq<0>{})),
- hana::make_lazy(hana::make_lazy(ct_eq<1>{})),
- hana::make_lazy(hana::make_lazy(ct_eq<2>{}))
- );
- //////////////////////////////////////////////////////////////////////////
- // Lazy methods
- //////////////////////////////////////////////////////////////////////////
- {
- // lazy
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::make_lazy(f)(),
- hana::make_lazy(f())
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::make_lazy(f)(ct_eq<0>{}),
- hana::make_lazy(f(ct_eq<0>{}))
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::make_lazy(f)(ct_eq<0>{}, ct_eq<1>{}),
- hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}))
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::make_lazy(f)(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}),
- hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}))
- ));
- // The function is not applied.
- hana::make_lazy(invalid)();
- hana::make_lazy(invalid)(ct_eq<0>{});
- hana::make_lazy(invalid)(ct_eq<0>{}, ct_eq<1>{});
- hana::make_lazy(invalid)(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{});
- }
- // eval
- {
- // With lazy expressions
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval(hana::make_lazy(ct_eq<0>{})),
- ct_eq<0>{}
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval(hana::make_lazy(ct_eq<1>{})),
- ct_eq<1>{}
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval(hana::make_lazy(f)()),
- f()
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval(hana::make_lazy(f)(ct_eq<3>{})),
- f(ct_eq<3>{})
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval(hana::make_lazy(f)(ct_eq<3>{}, ct_eq<4>{})),
- f(ct_eq<3>{}, ct_eq<4>{})
- ));
- // Should call a nullary function
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval([]{ return ct_eq<3>{}; }),
- ct_eq<3>{}
- ));
- // Should call a unary function with hana::id.
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval([](auto _) { return _(ct_eq<3>{}); }),
- ct_eq<3>{}
- ));
- // For overloaded function objects that are both nullary and unary,
- // the nullary overload should be preferred.
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::eval(f),
- f()
- ));
- }
- // Make sure this does not move from a destroyed object, as that
- // used to be the case.
- {
- auto x = hana::flatten(hana::make_lazy(hana::make_lazy(Tracked{1})));
- auto z = hana::eval(x); (void)z;
- }
- // In some cases where a type has a constructor that is way too
- // general, copying a lazy value holding an object of that type
- // could trigger the instantiation of that constructor. If that
- // constructor was ill-formed, the compilation would fail. We
- // make sure this does not happen.
- {
- {
- auto expr = hana::make_lazy(hana::test::trap_construct{});
- auto implicit_copy = expr; (void)implicit_copy;
- decltype(expr) explicit_copy(expr); (void)explicit_copy;
- }
- {
- auto expr = hana::make_lazy(hana::test::trap_construct{})();
- auto implicit_copy = expr; (void)implicit_copy;
- decltype(expr) explicit_copy(expr); (void)explicit_copy;
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Functor
- //////////////////////////////////////////////////////////////////////////
- {
- // transform
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::transform(hana::make_lazy(ct_eq<0>{}), f),
- hana::make_lazy(f(ct_eq<0>{}))
- ));
- }
- // laws
- hana::test::TestFunctor<hana::lazy_tag>{eqs, eq_elems};
- }
- //////////////////////////////////////////////////////////////////////////
- // Applicative
- //////////////////////////////////////////////////////////////////////////
- {
- // ap
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::ap(hana::make_lazy(f), hana::make_lazy(ct_eq<0>{})),
- hana::make_lazy(f(ct_eq<0>{}))
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::ap(hana::make_lazy(f), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{})),
- hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}))
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::ap(hana::make_lazy(f), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{}), hana::make_lazy(ct_eq<2>{})),
- hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}))
- ));
- // The function is not applied.
- hana::ap(hana::make_lazy(invalid), hana::make_lazy(ct_eq<0>{}));
- hana::ap(hana::make_lazy(invalid), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{}));
- hana::ap(hana::make_lazy(invalid), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{}), hana::make_lazy(ct_eq<2>{}));
- }
- // lift
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::lift<hana::lazy_tag>(ct_eq<0>{}),
- hana::make_lazy(ct_eq<0>{})
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::lift<hana::lazy_tag>(ct_eq<1>{}),
- hana::make_lazy(ct_eq<1>{})
- ));
- }
- // laws
- hana::test::TestApplicative<hana::lazy_tag>{eqs};
- }
- //////////////////////////////////////////////////////////////////////////
- // Monad
- //////////////////////////////////////////////////////////////////////////
- {
- auto f_ = hana::compose(hana::make_lazy, f);
- // chain
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::chain(hana::make_lazy(ct_eq<0>{}), f_),
- f_(ct_eq<0>{})
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::chain(hana::make_lazy(ct_eq<1>{}), f_),
- f_(ct_eq<1>{})
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::make_lazy(ct_eq<1>{}) | f_,
- f_(ct_eq<1>{})
- ));
- }
- // flatten
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::flatten(hana::make_lazy(hana::make_lazy(ct_eq<0>{}))),
- hana::make_lazy(ct_eq<0>{})
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::flatten(hana::make_lazy(hana::make_lazy(ct_eq<1>{}))),
- hana::make_lazy(ct_eq<1>{})
- ));
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::flatten(hana::make_lazy(hana::make_lazy(hana::make_lazy(ct_eq<1>{})))),
- hana::make_lazy(hana::make_lazy(ct_eq<1>{}))
- ));
- }
- // laws
- hana::test::TestMonad<hana::lazy_tag>{eqs, nested};
- }
- //////////////////////////////////////////////////////////////////////////
- // Comonad
- //////////////////////////////////////////////////////////////////////////
- {
- // extract
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::extract(hana::make_lazy(ct_eq<4>{})),
- ct_eq<4>{}
- ));
- }
- // duplicate
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::duplicate(hana::make_lazy(ct_eq<4>{})),
- hana::make_lazy(hana::make_lazy(ct_eq<4>{}))
- ));
- }
- // extend
- {
- BOOST_HANA_CONSTANT_CHECK(hana::equal(
- hana::extend(hana::make_lazy(ct_eq<4>{}), f),
- hana::make_lazy(f(hana::make_lazy(ct_eq<4>{})))
- ));
- }
- // laws
- hana::test::TestComonad<hana::lazy_tag>{eqs};
- }
- //////////////////////////////////////////////////////////////////////////
- // Make sure the monadic chain is evaluated in the right order.
- //////////////////////////////////////////////////////////////////////////
- {
- std::array<bool, 3> executed = {{false, false, false}};
- int dummy = 0;
- std::cout << "creating the monadic chain...\n";
- auto chain = hana::make_lazy(dummy)
- | [&](int dummy) {
- std::cout << "executing the first computation...\n";
- executed[0] = true;
- BOOST_HANA_RUNTIME_CHECK(
- executed == std::array<bool, 3>{{true, false, false}}
- );
- return hana::make_lazy(dummy);
- }
- | [&](int dummy) {
- std::cout << "executing the second computation...\n";
- executed[1] = true;
- BOOST_HANA_RUNTIME_CHECK(
- executed == std::array<bool, 3>{{true, true, false}}
- );
- return hana::make_lazy(dummy);
- }
- | [&](int dummy) {
- std::cout << "executing the third computation...\n";
- executed[2] = true;
- BOOST_HANA_RUNTIME_CHECK(
- executed == std::array<bool, 3>{{true, true, true}}
- );
- return hana::make_lazy(dummy);
- };
- BOOST_HANA_RUNTIME_CHECK(
- executed == std::array<bool, 3>{{false, false, false}}
- );
- std::cout << "evaluating the chain...\n";
- hana::eval(chain);
- }
- }
|