lazy.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // Copyright Louis Dionne 2013-2017
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
  4. #include <boost/hana/lazy.hpp>
  5. #include <boost/hana/ap.hpp>
  6. #include <boost/hana/assert.hpp>
  7. #include <boost/hana/chain.hpp>
  8. #include <boost/hana/concept/comparable.hpp>
  9. #include <boost/hana/config.hpp>
  10. #include <boost/hana/duplicate.hpp>
  11. #include <boost/hana/equal.hpp>
  12. #include <boost/hana/eval.hpp>
  13. #include <boost/hana/extend.hpp>
  14. #include <boost/hana/extract.hpp>
  15. #include <boost/hana/flatten.hpp>
  16. #include <boost/hana/functional/compose.hpp>
  17. #include <boost/hana/lift.hpp>
  18. #include <boost/hana/transform.hpp>
  19. #include <boost/hana/tuple.hpp>
  20. #include <laws/applicative.hpp>
  21. #include <laws/base.hpp>
  22. #include <laws/comonad.hpp>
  23. #include <laws/functor.hpp>
  24. #include <laws/monad.hpp>
  25. #include <support/tracked.hpp>
  26. #include <array>
  27. #include <iostream>
  28. namespace hana = boost::hana;
  29. using hana::test::ct_eq;
  30. namespace boost { namespace hana {
  31. // We provide this instance for unit tests only because it is _so_ much
  32. // more convenient, but this instance is too dangerous for general usage.
  33. // See the documentation of `hana::lazy` for more info.
  34. template <>
  35. struct equal_impl<lazy_tag, lazy_tag> {
  36. template <typename X, typename Y>
  37. static constexpr auto apply(X x, Y y)
  38. { return hana::equal(hana::eval(x), hana::eval(y)); }
  39. };
  40. }}
  41. auto invalid = [](auto x)
  42. { return x.this_function_must_not_be_instantiated; };
  43. int main() {
  44. hana::test::_injection<0> f{};
  45. auto eqs = hana::make_tuple(
  46. hana::make_lazy(ct_eq<0>{}),
  47. hana::make_lazy(ct_eq<1>{}),
  48. hana::make_lazy(ct_eq<2>{})
  49. );
  50. auto eq_elems = hana::make_tuple(ct_eq<0>{}, ct_eq<1>{}, ct_eq<1>{});
  51. auto nested = hana::make_tuple(
  52. hana::make_lazy(hana::make_lazy(ct_eq<0>{})),
  53. hana::make_lazy(hana::make_lazy(ct_eq<1>{})),
  54. hana::make_lazy(hana::make_lazy(ct_eq<2>{}))
  55. );
  56. //////////////////////////////////////////////////////////////////////////
  57. // Lazy methods
  58. //////////////////////////////////////////////////////////////////////////
  59. {
  60. // lazy
  61. {
  62. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  63. hana::make_lazy(f)(),
  64. hana::make_lazy(f())
  65. ));
  66. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  67. hana::make_lazy(f)(ct_eq<0>{}),
  68. hana::make_lazy(f(ct_eq<0>{}))
  69. ));
  70. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  71. hana::make_lazy(f)(ct_eq<0>{}, ct_eq<1>{}),
  72. hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}))
  73. ));
  74. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  75. hana::make_lazy(f)(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}),
  76. hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}))
  77. ));
  78. // The function is not applied.
  79. hana::make_lazy(invalid)();
  80. hana::make_lazy(invalid)(ct_eq<0>{});
  81. hana::make_lazy(invalid)(ct_eq<0>{}, ct_eq<1>{});
  82. hana::make_lazy(invalid)(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{});
  83. }
  84. // eval
  85. {
  86. // With lazy expressions
  87. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  88. hana::eval(hana::make_lazy(ct_eq<0>{})),
  89. ct_eq<0>{}
  90. ));
  91. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  92. hana::eval(hana::make_lazy(ct_eq<1>{})),
  93. ct_eq<1>{}
  94. ));
  95. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  96. hana::eval(hana::make_lazy(f)()),
  97. f()
  98. ));
  99. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  100. hana::eval(hana::make_lazy(f)(ct_eq<3>{})),
  101. f(ct_eq<3>{})
  102. ));
  103. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  104. hana::eval(hana::make_lazy(f)(ct_eq<3>{}, ct_eq<4>{})),
  105. f(ct_eq<3>{}, ct_eq<4>{})
  106. ));
  107. // Should call a nullary function
  108. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  109. hana::eval([]{ return ct_eq<3>{}; }),
  110. ct_eq<3>{}
  111. ));
  112. // Should call a unary function with hana::id.
  113. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  114. hana::eval([](auto _) { return _(ct_eq<3>{}); }),
  115. ct_eq<3>{}
  116. ));
  117. // For overloaded function objects that are both nullary and unary,
  118. // the nullary overload should be preferred.
  119. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  120. hana::eval(f),
  121. f()
  122. ));
  123. }
  124. // Make sure this does not move from a destroyed object, as that
  125. // used to be the case.
  126. {
  127. auto x = hana::flatten(hana::make_lazy(hana::make_lazy(Tracked{1})));
  128. auto z = hana::eval(x); (void)z;
  129. }
  130. // In some cases where a type has a constructor that is way too
  131. // general, copying a lazy value holding an object of that type
  132. // could trigger the instantiation of that constructor. If that
  133. // constructor was ill-formed, the compilation would fail. We
  134. // make sure this does not happen.
  135. {
  136. {
  137. auto expr = hana::make_lazy(hana::test::trap_construct{});
  138. auto implicit_copy = expr; (void)implicit_copy;
  139. decltype(expr) explicit_copy(expr); (void)explicit_copy;
  140. }
  141. {
  142. auto expr = hana::make_lazy(hana::test::trap_construct{})();
  143. auto implicit_copy = expr; (void)implicit_copy;
  144. decltype(expr) explicit_copy(expr); (void)explicit_copy;
  145. }
  146. }
  147. }
  148. //////////////////////////////////////////////////////////////////////////
  149. // Functor
  150. //////////////////////////////////////////////////////////////////////////
  151. {
  152. // transform
  153. {
  154. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  155. hana::transform(hana::make_lazy(ct_eq<0>{}), f),
  156. hana::make_lazy(f(ct_eq<0>{}))
  157. ));
  158. }
  159. // laws
  160. hana::test::TestFunctor<hana::lazy_tag>{eqs, eq_elems};
  161. }
  162. //////////////////////////////////////////////////////////////////////////
  163. // Applicative
  164. //////////////////////////////////////////////////////////////////////////
  165. {
  166. // ap
  167. {
  168. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  169. hana::ap(hana::make_lazy(f), hana::make_lazy(ct_eq<0>{})),
  170. hana::make_lazy(f(ct_eq<0>{}))
  171. ));
  172. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  173. hana::ap(hana::make_lazy(f), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{})),
  174. hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}))
  175. ));
  176. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  177. hana::ap(hana::make_lazy(f), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{}), hana::make_lazy(ct_eq<2>{})),
  178. hana::make_lazy(f(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}))
  179. ));
  180. // The function is not applied.
  181. hana::ap(hana::make_lazy(invalid), hana::make_lazy(ct_eq<0>{}));
  182. hana::ap(hana::make_lazy(invalid), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{}));
  183. hana::ap(hana::make_lazy(invalid), hana::make_lazy(ct_eq<0>{}), hana::make_lazy(ct_eq<1>{}), hana::make_lazy(ct_eq<2>{}));
  184. }
  185. // lift
  186. {
  187. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  188. hana::lift<hana::lazy_tag>(ct_eq<0>{}),
  189. hana::make_lazy(ct_eq<0>{})
  190. ));
  191. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  192. hana::lift<hana::lazy_tag>(ct_eq<1>{}),
  193. hana::make_lazy(ct_eq<1>{})
  194. ));
  195. }
  196. // laws
  197. hana::test::TestApplicative<hana::lazy_tag>{eqs};
  198. }
  199. //////////////////////////////////////////////////////////////////////////
  200. // Monad
  201. //////////////////////////////////////////////////////////////////////////
  202. {
  203. auto f_ = hana::compose(hana::make_lazy, f);
  204. // chain
  205. {
  206. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  207. hana::chain(hana::make_lazy(ct_eq<0>{}), f_),
  208. f_(ct_eq<0>{})
  209. ));
  210. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  211. hana::chain(hana::make_lazy(ct_eq<1>{}), f_),
  212. f_(ct_eq<1>{})
  213. ));
  214. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  215. hana::make_lazy(ct_eq<1>{}) | f_,
  216. f_(ct_eq<1>{})
  217. ));
  218. }
  219. // flatten
  220. {
  221. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  222. hana::flatten(hana::make_lazy(hana::make_lazy(ct_eq<0>{}))),
  223. hana::make_lazy(ct_eq<0>{})
  224. ));
  225. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  226. hana::flatten(hana::make_lazy(hana::make_lazy(ct_eq<1>{}))),
  227. hana::make_lazy(ct_eq<1>{})
  228. ));
  229. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  230. hana::flatten(hana::make_lazy(hana::make_lazy(hana::make_lazy(ct_eq<1>{})))),
  231. hana::make_lazy(hana::make_lazy(ct_eq<1>{}))
  232. ));
  233. }
  234. // laws
  235. hana::test::TestMonad<hana::lazy_tag>{eqs, nested};
  236. }
  237. //////////////////////////////////////////////////////////////////////////
  238. // Comonad
  239. //////////////////////////////////////////////////////////////////////////
  240. {
  241. // extract
  242. {
  243. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  244. hana::extract(hana::make_lazy(ct_eq<4>{})),
  245. ct_eq<4>{}
  246. ));
  247. }
  248. // duplicate
  249. {
  250. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  251. hana::duplicate(hana::make_lazy(ct_eq<4>{})),
  252. hana::make_lazy(hana::make_lazy(ct_eq<4>{}))
  253. ));
  254. }
  255. // extend
  256. {
  257. BOOST_HANA_CONSTANT_CHECK(hana::equal(
  258. hana::extend(hana::make_lazy(ct_eq<4>{}), f),
  259. hana::make_lazy(f(hana::make_lazy(ct_eq<4>{})))
  260. ));
  261. }
  262. // laws
  263. hana::test::TestComonad<hana::lazy_tag>{eqs};
  264. }
  265. //////////////////////////////////////////////////////////////////////////
  266. // Make sure the monadic chain is evaluated in the right order.
  267. //////////////////////////////////////////////////////////////////////////
  268. {
  269. std::array<bool, 3> executed = {{false, false, false}};
  270. int dummy = 0;
  271. std::cout << "creating the monadic chain...\n";
  272. auto chain = hana::make_lazy(dummy)
  273. | [&](int dummy) {
  274. std::cout << "executing the first computation...\n";
  275. executed[0] = true;
  276. BOOST_HANA_RUNTIME_CHECK(
  277. executed == std::array<bool, 3>{{true, false, false}}
  278. );
  279. return hana::make_lazy(dummy);
  280. }
  281. | [&](int dummy) {
  282. std::cout << "executing the second computation...\n";
  283. executed[1] = true;
  284. BOOST_HANA_RUNTIME_CHECK(
  285. executed == std::array<bool, 3>{{true, true, false}}
  286. );
  287. return hana::make_lazy(dummy);
  288. }
  289. | [&](int dummy) {
  290. std::cout << "executing the third computation...\n";
  291. executed[2] = true;
  292. BOOST_HANA_RUNTIME_CHECK(
  293. executed == std::array<bool, 3>{{true, true, true}}
  294. );
  295. return hana::make_lazy(dummy);
  296. };
  297. BOOST_HANA_RUNTIME_CHECK(
  298. executed == std::array<bool, 3>{{false, false, false}}
  299. );
  300. std::cout << "evaluating the chain...\n";
  301. hana::eval(chain);
  302. }
  303. }