Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free.

I've just learnt how lazy evaluation works and I was wondering: why isn't lazy evaluation applied in every software currently produced? Why still using eager evaluation?

share|improve this question
1  
Here's an example of what can happen if you mix mutable state and lazy evaluation. alicebobandmallory.com/articles/2011/01/01/… –  Jonas Elfström Mar 15 '13 at 9:08
1  
@JonasElfström: Please, do not confuse mutable state with one of its possible implementations. Mutable state can be implemented using an infinite, lazy stream of values. Then you do not have the problem of mutable variables. –  Giorgio Jun 7 at 9:55
    
In imperative programming languages, "lazy evaluation" requires a conscious effort from the programmer. Generic programming in imperative languages has made this easy, but it will never be transparent. The answer to the other side of the question brings out another question: "Why isn't functional programming languages used everywhere?", and the currently answer is simply "no" as a matter of current affairs. –  rwong Jun 11 at 6:28
    
Functional programming languages aren't used everywhere for the same reason we don't use hammers on screws, not every problem can easily be expressed in a functional input -> output manner, GUI for example is better suited to be expressed in an imperative manner. –  ALXGTV Jun 11 at 6:36
    
Further there are two classes of functional programming languages (or at least both claim to be functional), the imperative functional languages e.g. Clojure, Scala and the declarative e.g. Haskell, OCaml. –  ALXGTV Jun 11 at 6:38

6 Answers 6

up vote 26 down vote accepted

Lazy evaluation requires book-keeping overhead- you have to know if it's been evaluated yet and such things. Eager evaluation is always evaluated, so you don't have to know. This is especially true in concurrent contexts.

Secondly, it's trivial to convert eager evaluation into lazy evaluation by packaging it into a function object to be called later, if you so wish.

Thirdly, lazy evaluation implies a loss of control. What if I lazily evaluated reading a file from a disk? Or getting the time? That's not acceptable.

Eager evaluation can be more efficient and more controllable, and is trivially converted to lazy evaluation. Why would you want lazy evaluation?

share|improve this answer
3  
Lazily reading a file from disk is actually really neat--for most of my simple programs and scripts, Haskell's readFile is exactly what I need. Besides, converting from lazy to eager evaluation is just as trivial. –  Tikhon Jelvis Dec 12 '11 at 8:18
1  
Agree with you all except the last paragraph. Lazy evaluation is more efficient when there is a chain operation, and it can have more control of when you actually need the data –  texasbruce Feb 28 at 21:43

If we potentially have to fully evaluate an expression to determine it's value then lazy evaluation can be a disadvantage. Say we have a long list of boolean values and we want to find out if all of them are true:

[True, True, True, ... False]

In order to do this we have to look at every element in the list, no matter what, so there is no possibility of lazily cutting off evaluation. We can use a fold to determine if all the boolean values in the list are true. If we use a fold right, which uses lazy evaluation, we don't get any of the benefits of lazy evaluation because we have to look at every element in the list:

foldr (&&) True [True, True, True, ... False] 
> 0.27 secs

A fold right will be much slower in this case than a strict fold left, which does not use lazy evaluation:

foldl' (&&) True [True, True, True, ... False] 
> 0.09 secs

The reason is a strict fold left uses tail recursion, which means it accumulates the return value and doesn't build up and store in memory a large chain of operations. This is much faster than the lazy fold right because both functions have to look at the entire list anyway and the fold right can't use tail recursion. So, the point is, you should use whatever is best for the task at hand.

share|improve this answer
    
"So, the point is, you should use whatever is best for the task at hand." +1 –  Giorgio Jun 7 at 9:51

As @DeadMG noted Lazy evaluation requires book-keeping overhead. This can be expensive relative to eager evaluation. Consider this statement:

i = (243 * 414 + 6562 / 435.0 ) ^ 0.5 ** 3

This will take a bit of calculation to calculate. If I use lazy evaluation, then I need to check if it has been evaluated every time I use it. If this is inside a heavily used tight loop then the overhead increases significantly, but there is no benefit.

With eager evaluation and a decent compiler the formula is calculated at compile time. Most optimizers will move the assignment out of any loops it occurs in if appropriate.

Lazy evaluation is best suited to loading data which will be infrequently accessed and has a high overhead to retrieve. It is therefore more appropriate to edge cases than core functionality.

In general it is good practice to evaluate things that are frequently accessed as early as possible. Lazy evaluation does not work with this practice. If you will always access something, all lazy evaluation will do is add overhead. The cost/benefit of using lazy evaluation decreases as the item being accessed becomes less likely to be accessed.

Always using lazy evaluation also implies early optimization. This is a bad practice which often results in code which is much more complex and expensive that might otherwise be the case. Unfortunately, premature optimization often results in code that performs slower than simpler code. Until you can measure the effect of optimization, it is a bad idea to optimize your code.

Avoiding premature optimization does not conflict with good coding practices. If good practices were not applied, initial optimizations may consist of applying good coding practices such as moving calculations out of loops.

share|improve this answer
    
You seem to be arguing out of inexperience. I suggest you read the paper "Why Functional Programming Matters" by Wadler. It devotes a major section explaining the why of lazy evaluation (hint: it has little to do with performance, early optimization or "loading infrequently accessed data", and everything to do with modularity). –  Andres F. Jun 7 at 16:31
    
@AndresF I've read the paper you refer to. I agree with the use of lazy evaluation in such cases. Early evaluation may not be appropriate, but I would argue returning the sub-tree for the selected move may have a significant benefit if additional moves can be added easily. However, building that functionality could be premature optimization. Outside functional programming, I have seed significant issues with use of lazy evaluation, and the failure to use lazy evaluation. There are reports of significant performance costs resulting from lazy evaluation in functional programming. –  BillThor Jun 8 at 2:37
    
Such as? There are reports of significant performance costs when using eager evaluation as well (costs in the form of either unneeded evaluation, as well as program non-termination). There are costs to almost any other (mis)used feature, come to think of it. Modularity itself may come at a cost; the issue is whether it's worth it. –  Andres F. Jun 8 at 4:03

Here is a short comparison of the pros and cons of eager and lazy evaluation:

  • Eager evaluation:

    • Potential overhead of needlessly evaluating stuff.

    • Unhindered, fast evaluation.

  • Lazy evaluation:

    • No unnecessary evaluation.

    • Bookkeeping overhead at every use of a value.

So, if you have many expressions that never have to be evaluated, lazy is better; yet if you never have an expression that does not need to be evaluated, lazy is pure overhead.

Now, lets take a look at real world software: How many of the functions that you write do not require evaluation of all their arguments? Especially with the modern short functions that only do one thing, the percentage of functions fall into this category is very low. Thus, lazy evaluation would just introduce the bookkeeping overhead most of the time, without the chance to actually save anything.

Consequently, lazy evaluation simply does not pay on average, eager evaluation is the better fit for modern code.

share|improve this answer
1  
"Bookkeeping overhead at every use of a value.": I do not think the bookkeeping overhead is bigger than, say, checking for null references in a language like Java. In both cases you need to check one bit of information (evaluated / pending versus null / non-null) and you need to do it every time you use a value. So, yes, there is a overhead, but it is minimal. –  Giorgio Jun 7 at 9:43
    
"How many of the functions that you write do not require evaluation of all their arguments?": This is just one example application. What about recursive, infinite data structures? Can you implement them with eager evaluation? You can use iterators, but the solution is not always as concise. Of course you probably do not miss something that you have never had the chance to use extensively. –  Giorgio Jun 7 at 9:46
1  
"Consequently, lazy evaluation simply does not pay on average, eager evaluation is the better fit for modern code.": This statement does not hold: it really depends on what you are trying to implement. –  Giorgio Jun 7 at 9:47
1  
@Giorgio The overhead may not seem much to you, but conditionals are one of the things modern CPUs suck at: A mispredicted branch usually forces a complete pipeline flush, throwing away the work of more than ten CPU cycles. You don't want unnecessary conditions in your inner loop. Paying ten cycles extra per function argument is almost as unacceptable for performance sensitive code as coding the thing in java. You are right that lazy evaluation allows you to pull off some tricks that you can't easily do with eager evaluation. But the vast majority of code does not need these tricks. –  cmaster Jun 7 at 12:53
1  
This seems to be an answer out of inexperience with languages with lazy evaluation. For example, what about infinite data structures? –  Andres F. Jun 7 at 16:34

Mainly because lazy code and state can mix badly and cause some hard to find bugs. If the state of a dependent object changes the value of your lazy object can be wrong when evaluated. It's much better to have the programmer explicitly code the object to be lazy when he/she knows the situation is appropriate.

On a side note Haskell uses Lazy evaluation for everything. This is possible because it's a functional language and doesn't use state (except in a few exceptional circumstances where they are clearly marked)

share|improve this answer
    
Yeah, mutable state + lazy evaluation = death. I think the only points I lost on my SICP final were about using set! in a lazy Scheme interpreter. >:( –  Tikhon Jelvis Dec 12 '11 at 8:14
1  
"lazy code and state can mix badly": It really depends on how you implement state. If you implement it using shared mutable variables, and you depend on the order of evaluation for your state to be consistent, then you are right. –  Giorgio Jun 7 at 9:38

Lazy evaluation's is not always better.

The performance benefits of lazy evaluation can be great, but it is not hard to avoid most unnecessary evaluation in eager environments- surely lazy makes it easy and complete, but rarely is unnecessary evaluation in code a major problem.

The good thing about lazy evaluation is when it lets you write clearer code; getting the 10th prime by filtering an infinite natural numbers list and taking the 10th element of that list is one of the most concise and clear way of proceeding: (pseudocode)

let numbers = [1,2...]
fun is_prime x = all (map (y-> y mod x == 0) [1..x])
let primes = filter is_prime numbers
let tenth_prime = first (take primes 10)

I believe it would be quite difficult to express things so concisely without lazyness.

But lazyness isn't the answer to everything. For starters, lazyness cannot be applied transparently in the presence of state, and I believe statefulness cannot be automatically detected (unless you are working in say, Haskell, when state is quite explicit). So, in most languages, lazyness needs to be done manually, which makes things less clear and thus removes one of the big benefits of lazy eval.

Furthermore, lazyness has performance drawbacks, as it incurs a significant overhead of keeping non-evaluated expressions around; they use up storage and they are slower to work with than simple values. It is not uncommon to find out that you have to eager-ify code because the lazy version is dog slow- and it is sometimes hard to reason about performance.

As it tends to happen, there is no absolute best strategy. Lazy is great if you can write better code taking advantage of infinite data structures or other strategies it allows you to use, but eager can be easier to optimize.

share|improve this answer
    
+1 for the first and last paragraphs (-: –  Murph Dec 11 '11 at 22:29
    
Would it be possible for a really clever compiler to mitigate the overhead significantly. or even take advantage of laziness for extra optimizations? –  Tikhon Jelvis Dec 12 '11 at 8:20

protected by gnat Jun 7 at 9:13

Thank you for your interest in this question. Because it has attracted low-quality answers, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.