views:

3159

answers:

8

C++ does not have native support for lazy evaluation (as Haskell does).

I'm wondering if it is possible to implement lazy evaluation in C++ in a reasonable manner. If yes, how would you do it?

EDIT: I like Konrad Rudolph's answer.

I'm wondering if it's possible to implement it in a more generic fashion, for example by using a parametrized class lazy that essentially works for T the way matrix_add works for matrix.

Any operation on T would return lazy instead. The only problem is to store the arguments and operation code inside lazy itself. Can anyone see how to improve this?

+1  A: 

As it's going to be done in C++0x, by lambda expressions.

Mehrdad Afshari
I'll give you a point if you tell me what possible values x has :D
It's -1 probably ;)
Mehrdad Afshari
lol @Mehrdad -1
acidzombie24
Theres also a joke about c++0x being a hex value
acidzombie24
+2  A: 

Anything is possable.

It depends on exactly what you mean:

class X
{
     public: static X& getObjectA()
     {
          static X instanceA;

          return instanceA;
     }
};

Here we have the affect of a global variable that is lazily evaluated at the point of first use.

As newly requested in the question.
And stealing Konrad Rudolph design and extending it.

The Lazy object:

template<typename O,typename T1,typename T2>
struct Lazy
{
    Lazy(T1 const& l,T2 const& r)
        :lhs(l),rhs(r) {}

    typedef typename O::Result  Result;
    operator Result() const
    {
        O   op;
        return op(lhs,rhs);
    }
    private:
        T1 const&   lhs;
        T2 const&   rhs;
};

How to use it:

namespace M
{
    class Matrix
    {
    };
    struct MatrixAdd
    {
        typedef Matrix  Result;
        Result operator()(Matrix const& lhs,Matrix const& rhs) const
        {
            Result  r;
            return r;
        }
    };
    struct MatrixSub
    {
        typedef Matrix  Result;
        Result operator()(Matrix const& lhs,Matrix const& rhs) const
        {
            Result  r;
            return r;
        }
    };
    template<typename T1,typename T2>
    Lazy<MatrixAdd,T1,T2> operator+(T1 const& lhs,T2 const& rhs)
    {
        return Lazy<MatrixAdd,T1,T2>(lhs,rhs);
    }
    template<typename T1,typename T2>
    Lazy<MatrixSub,T1,T2> operator-(T1 const& lhs,T2 const& rhs)
    {
        return Lazy<MatrixSub,T1,T2>(lhs,rhs);
    }
}
Martin York
That's the technique I've used. (although I wouldn't say "anything" is possible. ;)
Jason S
another nice "lazy creation" is to create a large vector of boost::optional<T> . they demonstrate it on their website
Johannes Schaub - litb
A: 

It's easy enough to create your own "container" class that takes a generating function object and exposes iterators.

Eclipse
+20  A: 

I'm wondering if it is possible to implement lazy evaluation in C++ in a reasonable manner. If yes, how would you do it?

Yes, this is possible and quite often done, e.g. for matrix calculations. The main mechanism to facilitate this is operator overloading. Consider the case of matrix addition. The signature of the function would usually look something like this:

matrix operator +(matrix const& a, matrix const& b);

Now, to make this function lazy, it's enough to return a proxy instead of the actual result:

struct matrix_add;

matrix_add operator +(matrix const& a, matrix const& b) {
    return matrix_add(a, b);
}

Now all that needs to be done is to write this proxy:

struct matrix_add {
    matrix_add(matrix const& a, matrix const& b) : a(a), b(b) { }

    operator matrix() const {
        matrix result;
        // Do the addition.
        return result;
    }
private:
    matrix const& a, b;
};

The magic lies in the method operator matrix() which is an implicit conversion operator from matrix_add to plain matrix. This way, you can chain multiple operations (by providing appropriate overloads of course). The evaluation takes place only when the final result is assigned to a matrix instance.

EDIT I realize that I should have been more explicit. As it is, the code makes no sense because although evaluation happens lazy, it still happens in the same expression. In particular, another addition will evaluate this code unless the matrix_add structure is changed to allow chained addition. C++0x greatly facilitates this by allowing variadic templates (i.e. template lists of variable length).

However, one very simple case where this code would actually have a real, direct benefit is the following:

int value = (A + B)(2, 3);

Here, it is assumed that A and B are two-dimensional matrices and that dereferencing is done in Fortran notation, i.e. the above calculates one element out of a matrix sum. It's of course wasteful to add the whole matrices. matrix_add to the rescue:

struct matrix_add {
    // … yadda, yadda, yadda …

    int operator ()(unsigned int x, unsigned int y) {
        // Calculate *just one* element:
        return a(x, y) + b(x, y);
    }
};

Other examples abound. I've just remembered that I have implemented something related not long ago. Basically, I had to implement a string class that should adhere to a fixed, pre-defined interface. However, my particular string class dealt with huge strings that weren't actually stored in memory. Usually, the user would just access small substrings from the original string using a function infix. I overloaded this function for my string type to return a proxy that held a reference to my string, along with the desired start and end position. Only when this substring was actually used did it query a C API to retrieve this portion of the string.

Konrad Rudolph
This is the style of answer I was looking for. I especially like the trick with the cast operator overloading.
Actually, I'm not fully satisfied with the above solution. Because if you do "matrix_add D = A + (B + C)", I imagine B + C is evaluated right there. Maybe the way to go is to have the + operator accept matrix_add's as parameters.
I think the above is a bare bones example. It is easily extensable to your suggestion of A + B + C;
Martin York
The other problem with this is that it will reevaluate the lazy operation each time the value is requested, rather than just the first time.
Chris Dodd
@Chris: yes, this is another problem. It too can be solved (e.g. by caching a value once calculated) but this only makes my example more complicated. ;-)
Konrad Rudolph
@Chris - Thats actually the point of lazy evaluation. So you can change a value in one of them, and it will be reevaluated each time. If you don't want this, then just do it concretely once.
Steve
@Steve: no, it isn’t. Haskell uses lazy evaluation everywhere but that doesn’t mean that everything is re-evaluated every time the value is needed. Rather, some functions *expect* that re-evaluation doesn’t happen if not necessary. For example, check out the “canonical” implementation of the Fibonacci sequence: `1 : 1 : [ a+b | (a,b) <- zip fib (tail fib) ]` – using this definition to execute `take 50 fib` clearly shows that re-evaluation does not happen, otherwise runtime would be exponential instead of linear (as observed).
Konrad Rudolph
+3  A: 

C++0x is nice and all.... but for those of us living in the present you have Boost lambda library and Boost Phoenix. Both with the intent of bringing large amounts of functional programming to C++.

Hippiehunter
+9  A: 

What Konrad already explained can be put further to support nested invocations of operators, all executed lazily. In Konrad's example, he has an expression object that can store exactly two arguments, for exactly two operands of one operation. The problem is that it will only execute one subexpression lazily, which nicely explains the concept in lazy evaluation put in simple terms, but doesn't improve performance substantially. The other example shows also well how one can apply operator() to add only some elements using that expression object. But to evaluate arbitrary complex expressions, we need some mechanism that can store the structure of that too. We can't get around templates to do that. And the name for that is expression templates. The idea is that one templated expression object can store the structure of some arbitrary sub-expression recursively, like a tree, where the operations are the nodes, and the operands are the child-nodes. For a very good explanation i just found today (some days after i wrote the below code) see here.

template<typename Lhs, typename Rhs>
struct AddOp {
    Lhs const& lhs;
    Rhs const& rhs;

    AddOp(Lhs const& lhs, Rhs const& rhs):lhs(lhs), rhs(rhs) {
        // empty body
    }

    Lhs const& get_lhs() const { return lhs; }
    Rhs const& get_rhs() const { return rhs; }
};

That will store any addition operation, even nested one, as can be seen by the following definition of an operator+ for a simple point type:

struct Point { int x, y; };

// add expression template with point at the right
template<typename Lhs, typename Rhs> AddOp<AddOp<Lhs, Rhs>, Point> 
operator+(AddOp<Lhs, Rhs> const& lhs, Point const& p) {
    return AddOp<AddOp<Lhs, Rhs>, Point>(lhs, p);
} 

// add expression template with point at the left
template<typename Lhs, typename Rhs> AddOp< Point, AddOp<Lhs, Rhs> > 
operator+(Point const& p, AddOp<Lhs, Rhs> const& rhs) {
    return AddOp< Point, AddOp<Lhs, Rhs> >(p, rhs);
}

// add two points, yield a expression template    
AddOp< Point, Point > 
operator+(Point const& lhs, Point const& rhs) {
    return AddOp<Point, Point>(lhs, rhs);
}

Now, if you have

Point p1 = { 1, 2 }, p2 = { 3, 4 }, p3 = { 5, 6 };
p1 + (p2 + p3); // returns AddOp< Point, AddOp<Point, Point> >

You now just need to overload operator= and add a suitable constructor for the Point type and accept AddOp. Change its definition to:

struct Point { 
    int x, y; 

    Point(int x = 0, int y = 0):x(x), y(y) { }

    template<typename Lhs, typename Rhs>
    Point(AddOp<Lhs, Rhs> const& op) {
        x = op.get_x();
        y = op.get_y();
    }

    template<typename Lhs, typename Rhs>
    Point& operator=(AddOp<Lhs, Rhs> const& op) {
        x = op.get_x();
        y = op.get_y();
        return *this;
    }

    int get_x() const { return x; }
    int get_y() const { return y; }
};

And add the appropriate get_x and get_y into AddOp as member functions:

int get_x() const {
    return lhs.get_x() + rhs.get_x();
}

int get_y() const {
    return lhs.get_y() + rhs.get_y();
}

Note how we haven't created any temporaries of type Point. It could have been a big matrix with many fields. But at the time the result is needed, we calculate it lazily.

Johannes Schaub - litb
Nice! However, when you want to lazily store p1 + (p2 + p3), how do yo specify its type :D?
yeah, that's actually a pity in current C++. you you would have to write out the type, or have to use polymorphism to store it without writing it out. Next C++ has auto, so you can do auto p = a + b; but that would have other problems: the temporaries created by "a + b" (the expression templates)...
Johannes Schaub - litb
would be destroyed after the full expression, and then we would be left with dangling references. so it gets hairy quite easily :)
Johannes Schaub - litb
you can parameterize the expression templates on whether they use references or not. then you can create a "lazy expression type" lazy_expr e = a + (b + c); when assigned to that one, the expression template would be deep copied. i.e all references to sub-expressions would be copied.
Johannes Schaub - litb
except the references to plain Point objects, which would still be held by reference. that way it could work. storing the expression into lazy_expr requires it to have a templated constructor, and creating a polymorphic type wrapping the expression internally. When actually evaluating it, ...
Johannes Schaub - litb
you would need one virtual function call to get the machinery started.
Johannes Schaub - litb
+6  A: 

I have nothing to add to Konrad's post, but you can look at Eigen for an example of lazy evaluation done right, in a real world app. It is pretty awe inspiring.

+7  A: 

Boost.Lambda is very nice, but Boost.Proto is exactly what you are looking for. It already has overloads of all C++ operators, which by default perform their usual function when proto::eval() is called, but can be changed.

j_random_hacker
+1, Any reference to a Boost library that gets half the work done for you in a portable and safe way is good to have.
Matthieu M.