Boost.Hana  1.0.1 Your standard library for metaprogramming
boost::hana::lazy< implementation_defined > Struct Template Reference

Description

template<typename implementation_defined> struct boost::hana::lazy< implementation_defined >

hana::lazy implements superficial laziness via a monadic interface.

It is important to understand that the laziness implemented by lazy is only superficial; only function applications made inside the lazy monad can be made lazy, not all their subexpressions.

Note
The actual representation of hana::lazy is completely implementation-defined. Lazy values may only be created through hana::make_lazy, and they can be stored in variables using auto, but any other assumption about the representation of hana::lazy<...> should be avoided. In particular, one should not rely on the fact that hana::lazy<...> can be pattern-matched on, because it may be a dependent type.

Modeled concepts

1. Functor
Applying a function over a lazy value with transform returns the result of applying the function, as a lazy value.
namespace hana = boost::hana;
using hana::_;
int main() {
static_assert(hana::eval(hana::transform(hana::make_lazy(4 / _)(1), _ * 3)) == (4 / 1) * 3, "");
hana::transform(hana::make_lazy(4 / _)(0), _ * 3); // never evaluated
}
2. Applicative
A normal value can be lifted into a lazy value by using lift<lazy_tag>. A lazy function can be lazily applied to a lazy value by using ap.
3. Monad
The lazy monad allows combining lazy computations into larger lazy computations. Note that the | operator can be used in place of the chain function.
#include <functional>
#include <iostream>
#include <sstream>
namespace hana = boost::hana;
template <typename T>
T x;
stream >> x;
std::cout << "read " << x << " from the stream\n";
return x;
}
int main() {
std::stringstream ss;
int in = 123;
std::cout << "creating the monadic chain...\n";
| [](auto x) {
std::cout << "performing x + 1...\n";
return hana::make_lazy(x + 1);
}
| [](auto x) {
std::cout << "performing x / 2...\n";
return hana::make_lazy(x / 2);
};
std::cout << "putting " << in << " in the stream...\n";
ss << in; // nothing is evaluated yet
std::cout << "evaluating the monadic chain...\n";
auto eout = hana::eval(out);
std::cout << "the result of the monadic chain is " << eout << "\n";
BOOST_HANA_RUNTIME_CHECK(eout == (in + 1) / 2);
}
4. Comonad
The lazy comonad allows evaluating a lazy computation to get its result and lazily applying functions taking lazy inputs to lazy values. This blog post goes into more details about lazy evaluation and comonads.
#include <sstream>
namespace hana = boost::hana;
int main() {
std::stringstream s("1 2 3");
auto i = hana::make_lazy([&] {
int i;
s >> i;
return i;
})();
auto i_plus_one = hana::extend(i, [](auto lazy_int) {
return hana::eval(lazy_int) + 1;
});
}
Note
hana::lazy only models a few concepts because providing more functionality would require evaluating the lazy values in most cases. Since this raises some issues such as side effects and memoization, the interface is kept minimal.

Synopsis of associated functions

constexpr auto eval
Evaluate a lazy value and return it. More...

template<>
constexpr auto make< lazy_tag >
Lifts a normal value to a lazy one. More...

constexpr auto make_lazy = make<lazy_tag>
Alias to make<lazy_tag>; provided for convenience. More...

Friends

template<typename... T, typename F >
constexpr auto operator| (lazy< T... >, F)
Equivalent to hana::chain.

Associated functions

template<typename implementation_defined >
 constexpr auto eval
related
Initial value:
= [](auto&& see_documentation) -> decltype(auto) {
return tag-dispatched;
}

Evaluate a lazy value and return it.

Given a lazy expression expr, eval evaluates expr and returns the result as a normal value. However, for convenience, eval can also be used with nullary and unary function objects. Specifically, if expr is not a hana::lazy, it is called with no arguments at all and the result of that call (expr()) is returned. Otherwise, if expr() is ill-formed, then expr(hana::id) is returned instead. If that expression is ill-formed, then a compile-time error is triggered.

The reason for allowing nullary callables in eval is because this allows using nullary lambdas as lazy branches to eval_if, which is convenient. The reason for allowing unary callables and calling them with hana::id is because this allows deferring the compile-time evaluation of selected expressions inside the callable. How this can be achieved is documented by hana::eval_if.

Example

namespace hana = boost::hana;
static_assert(hana::eval(hana::make_lazy(hana::_ + 1)(3)) == 4, "");
int main() { }
template<typename implementation_defined >
template<>
 constexpr auto make< lazy_tag >
related
Initial value:
= [](auto&& x) {
return lazy<implementation_defined>{forwarded(x)};
}

Lifts a normal value to a lazy one.

make<lazy_tag> can be used to lift a normal value or a function call into a lazy expression. Precisely, make<lazy_tag>(x) is a lazy value equal to x, and make<lazy_tag>(f)(x1, ..., xN) is a lazy function call that is equal to f(x1, ..., xN) when it is evaluated.

Note
It is interesting to note that make<lazy_tag>(f)(x1, ..., xN) is equivalent to
ap(make<lazy_tag>(f), lift<lazy_tag>(x1), ..., lift<lazy_tag>(xN))
which in turn is equivalent to make<lazy_tag>(f(x1, ..., xN)), except for the fact that the inner call to f is evaluated lazily.

Example

namespace hana = boost::hana;
int main() {
BOOST_HANA_CONSTEXPR_LAMBDA auto f = hana::make<hana::lazy_tag>([](auto x) {
return 1 / x;
});
BOOST_HANA_CONSTEXPR_LAMBDA auto g = hana::make_lazy([](auto x) {
return x + 1;
});
BOOST_HANA_CONSTEXPR_CHECK(hana::eval(hana::if_(hana::false_c, f(0), g(0))) == 0 + 1);
}
template<typename implementation_defined >
 constexpr auto make_lazy = make
related

Alias to make<lazy_tag>; provided for convenience.