
Streams – Lazy evaluation in C++14 - normanv
http://jscheiny.github.io/Streams/
======
wwwwwwwwww
honest question: is there any reason to add lazy evaluation to c++ other than
to just add more features to the language?

lazy evaluation is one of those features where i have absolutely no idea what
benefit it actually provides

~~~
wyager
>lazy evaluation is one of those features where i have absolutely no idea what
benefit it actually provides

Good question.

Let's say you have a program like this little python example:

    
    
        inputs = [raw_read() for i in range(1000)] # Read numbers from command line
        numbers = map(int, inputs) # Turn them into integers
        for num in numbers: # Check for 42
          if num == 42:
            return true
        return false # It's not there
    

If you use lazy evaluation, each item in the "map" object will only be
calculated as needed. So if the first item in the "inputs" list is "42", it
will only do a single string-to-integer conversion.

If you use greedy evaluation, it will map all strings to integers at once,
which might be wasteful.

There are other benefits as well; this is the most obvious.

~~~
ori_b
But in a language with mutating side effects, and iterators, I'm not sure what
this buys you over:

    
    
        for (auto& num = file.line_iterator(); num != file.end(); num++) {
            if (atoi(num) == 42)
                return true;
        return false;
    

Note that 'line_iterator()' isn't something that (as far as I know) is defined
in the C++ standard library, but there's nothing preventing it.

~~~
hrjet
> But in a language with mutating side effects, and iterators, I'm not sure
> what this buys you...

Side-effect free, functional style of programming.

------
mattpodwysocki
Seems very close in implementation to RxCPP for push based streams and IxCPP
for pull based, which has been created by Microsoft and now controlled by
Microsoft Open Technologies. This library though does not require C++14 and is
being actively developed: [https://github.com/Reactive-
Extensions/RxCPP](https://github.com/Reactive-Extensions/RxCPP)

------
srean
@wwwwwwwwww you have been hell-banned and I dont see anything particularly
offensive in your immediate comment history.

> is there any reason to add lazy evaluation to c++ [...]

I think you are suffering from a misapprehension. Nothing is being added to
C++ the language. This is just an implementation of lazy streams in C++14.
Think of it as an optional 3rd party library.

> lazy evaluation is one of those features where i have absolutely no idea
> what benefit it actually provides

Let me give you two examples. First try and solve this problem in your
favorite language. Produce a sequence of k numbers that are multiples of 2,3
and 5 only, but produce them in sorted order. Yes of course this is contrived,
but it will give you and idea of the conveniences that laziness can give you.
[sorry, I gave away the important spoiler]

Second example say you want to compute

    
    
       (x + y)*z - w
    

where each of them are objects representing long vectors. You also want to
keep the code readable and close to the mathematical notation of the domain.
Naive way to do it will create lots of temporaries and unnecessary loops.
Every binary operation is going to create a temporary (and entail a loop). But
if you have laziness you just need one loop.

[EDIT: _If one has laziness one need not instantiate the actual vector (x+y).
One can return an object that only represents the operation. So one can keep
building on the parse tree and finally when the result is actually used
somewhere do you evaluate the expression encoded in the parse tree. Now since
you have the entire expression tree you can evaluate it in just one loop. If
you are curious you can search for deforestation and expression templates. You
can of course do the same thing even if the language does not support
laziness, but laziness is the key enabler here, if the language does not
support it, _you_ have to implement it_ ]. Without this features it is pretty
much impossible to approach Fortran like speeds and expressiveness in array
operations in C++.

A reason why basic Numpy is slow is because is that written naively it creates
all these temporaries. There are a few ways to avoid it. Numexpr uses laziness
(and tiling). You could also use the 'out' feature of Numpy functions
creatively.

So in short, laziness has a lot of practical value, and I am not even bringing
up aspects of purity etc. I had to roll something on my own for these needs.
Would have been happier if there were such libraries available. [Ok full
disclosure, I would have likely rolled my own anyway to understand how it
works] There are a few libraries that do these things or help a lot in
building these things. Some of the are Spirit and Fusion. Although I am
conflating two different topics (i) fusion/deforestation and (ii) laziness,
but they are related enough.

@dang thanks for the clarification, wwwwwwww's comment was indeed showing up
as dead on this thread. wwwwwwww must have deleted his comment because at that
time there was only one.

~~~
ptfoobar
_Second example say you want to compute_

(x + y) * z - w

 _where each of them are long vectors but want to keep the look of the code
readable and explicit. Naive way to do it will create lots of temporaries and
unnecessary loops. Every binary operation is going to create a temporary (and
entail a loop). But if you have laziness you just need one loop._

Can you elaborate on this? Why can laziness provide better code here?

~~~
jzwinck
If each of the four variables is a vector, naively you might do (x + y) first,
producing a new vector. Then you would multiply by z, producing a second new
vector. Finally, subtract w, producing a third new vector. You have now
iterated over the length of the vectors three times, and allocated three new
vectors (two of which are no longer needed).

A better way to do all that would be to allocate a single result vector and
populate it with the full computed expression for each element. This can be
much faster for large vectors. Python's numexpr (among others) is designed to
do just this.

------
aetherspawn
The performance isn't going to be very good without stream fusion, I would
imagine.

------
ris
Ah, python's itertools available in c++, at last.

~~~
pixelglow
There's a previous implementation of itertools in C++:
[https://github.com/ryanhaining/cppitertools](https://github.com/ryanhaining/cppitertools).

~~~
srean
Thanks for the reminder. I had come across cppitertools before and wanted to
explore it and then totally forgot. Not sure why you got downvoted here, have
tried to compensate.

BTW your handle rung a bell. I was wracking my brain to remember where have I
heard of it before, then finally, oh yes MacSTL. It really deserves more love.
Have not used it, but sure browsed the code.

