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