Delay execution is almost always a boon. But then there are cases when it’s a problem and you resort to “fetch” (in Nhibernate) to eager fetch it.
Do you know practical situations when lazy evaluation can bite you back…?
Delay execution is almost always a boon. But then there are cases when it’s a problem and you resort to “fetch” (in Nhibernate) to eager fetch it.
Do you know practical situations when lazy evaluation can bite you back…?
Lazy loading resources involves a trip back and forth between the requester and the source for each load. In the case of NHibernate, this means a from the application to the database (which is often on a different server).
There is often overhead associated with each trip (there certainly is for NHibernate or any other DB query).
If you know that you will need all or a substantial portion of the data, you are better off pulling it in one go and only incurring the overhead once.
A classic example is when you need to pull back a list of objects to populate a combo box (often these will be configuration objects). Lazy loading would go back to the database each time you added a list member to the combo box. Since you're putting the entire list into the combo box, you would incur lots of extra overhead to lazy fetch each object.
It can also be a problem with the user experience of your program. People will happily wait for 5 seconds when a banner is displayed on the screen during app loading, but they despise to have to wait 0.25 seconds when they're typing in something in a textbox. If the amount of time it takes to load all your data eagerly is not that long, you may consider doing it at some point in the workflow where people accept a delay (such as app loading, window pop up, button presses).
Lazy evaluation is not useful in situations where performance is critical and a value must always be evaluated. In these cases you are better off just evaluating the value and being done with it, because the overhead of lazy evaluation will be wasted.
Lazy evaluation is not useful when you don't want to store value, just use it. But this depends on the implementation of the lazy evaluator. Some systems (like Haskell) can tell if a value will be used again. Some others cannot and can cause leaks.
Lazy evaluation is not useful when evaluation can have side-effects. That's the only reason and that's why only purely functional languages have it. If expressions can have side-effects which must occur in a certain order you can't have it.
Apart from that, lazy evaluation only gains performance, that's it's primary goal. And that's why some languages forbid side-effects, to gain lazy evaluation for that compromise, another nice effect of it is that control structures can be regular functions.
One example for lazyness causing weird problems (happened to me today, in Haskell):
import System.IO
main = do
content <- readFile "foo.txt"
writeFile "foo.txt" content
This throws the following error when compiled & executed:
foo.txt: openFile: resource busy (file is locked)
What I thought it would do: Open file foo.txt, read content, close it again. Then open it for writing, write the content, and close it again.
What it actually did: "Ah, some content. I'll probably read it later when we actually need it." Then open "foo.txt" for writing. Start writing content... ok, now we need the content. Open foo.txt for reading - bam!
I know it's trivial to fix, but it's hard to find if you don't know where to look.
You cannot do a reduction (eg. a fold) on input data in constant space with lazy evaluation, as the delayed evaluation of each reduction step results in linear space complexity. You must instead force-evaluate the result of each reduction step to keep the space usage constant.
For example, hashing a file in Haskell. You might be well meaning and read the input file lazily chunk-by-chunk, adding each chunk to the digest, but behind your back Haskell is actually making a thunk for every chunk you add to the digest, leaving the entire file in memory in these thunks until the resulting digest is actually evaluated. Ouch!
See last comment here: http://stackoverflow.com/questions/2981582/haskell-lazy-i-o-and-closing-files/2983328#2983328