]>
Commit | Line | Data |
---|---|---|
b32b8144 | 1 | // Copyright Louis Dionne 2013-2017 |
7c673cae FG |
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> | |
43 | 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) | |
49 | { return x.this_function_must_not_be_instantiated; }; | |
50 | ||
51 | ||
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 | } |