Why is the concept of lazy evaluation useful?

It seems lazy evaluation of expressions can cause a programmer to lose control over the order in which their code is executed. I am having trouble understanding why this would be acceptable or desired by a programmer.

How can this paradigm be used to build predictable software that works as intended, when we have no guarantee when and where an expression will be evaluated?

9

A lot of the answers are going into things like infinite lists and performance gains from unevaluated parts of the computation, but this is missing the larger motivation for laziness: modularity.

The classic argument is laid out in the much-cited paper “Why Functional Programming Matters” (PDF link) by John Hughes. The key example in that paper (Section 5) is playing Tic-Tac-Toe using the alpha-beta search algorithm. The key point is (p. 9):

[Lazy evaluation] makes it practical to modularize a program as a generator that constructs a large number of possible answers, and a selector that chooses the appropriate one.

The Tic-Tac-Toe program can be written as a function that generates the whole game tree starting at a given position, and a separate function that consumes it. At runtime this does not intrinsically generate the whole game tree, only those subparts that the consumer actually needs. We can change the order and combination in which alternatives are produced by changing the consumer; no need to change the generator at all.

In an eager language, you can’t write it this way because you would probably spend too much time and memory generating the tree. So you end up either:

  1. Combining the generation and consumption into the same function;
  2. Writing a producer that works optimally only for certain consumers;
  3. Implementing your own version of laziness.

7

How can this paradigm be used to build predictable software that works as intended, when we have no guarantee when and where an expression will be evaluated?

When an expression is side-effect free, the order in which the expressions are evaluated does not affect their value, so the behavior of the program is not affected by the order. So the behavior is perfectly predictable.

Now side effects are a different matter. If side effects could occur in any order, the behavior of the program would indeed be unpredictable. But this is not actually the case. Lazy languages like Haskell make it a point to be referentially transparent, i.e. making sure that the order in which expressions are evaluated will never affect their result. In Haskell this is achieved by forcing all operations with user-visible side effects to occur inside the IO monad. This makes sure that all side-effects occur exactly in the order you’d expect.

8

If you are familiar with databases, a very frequent way to process data is:

  • Ask a question like select * from foobar
  • While there is more data, do: get next row of results and process it

You do not control how the result is generated and in which way (indexes? Full table scans?), or when (should all the data be generated at once or incrementally when being asked for?). All you know is: if there is more data, you will get it when you ask for it.

Lazy evaluation is pretty close to the same thing. Say you have an infinite list defined as ie. the Fibonacci sequence – if you need five numbers, you get five numbers calculated; if you need 1000 you get 1000. The trick is that the runtime knows what to provide where and when. It is very, very handy.

(Java programmers can emulate this behavior with Iterators – other languages may have something similar)

3

Consider asking your database for a list of the first 2000 users whose names start with “Ab” and are older than 20 years. Also they must be male.

Here’s a little diagram.

You                                            Program Processor
------------------------------------------------------------------------------
Get the first 2000 users ---------->---------- OK!
                         --------------------- So I'll go get those records...
WAIT! Also, they have to ---------->---------- Gotcha!
start with "Ab"
                         --------------------- NOW I'll get them...
WAIT! Make sure they're  ---------->---------- Good idea Boss!
over 20!
                         --------------------- Let's go then...
And one more thing! Make ---------->---------- Anything else? Ugh!
sure they're male!

No that is all. :(       ---------->---------- FINE! Getting records!

                         --------------------- Here you go. 
Thanks Postgres, you're  ---------->----------  ...
my only friend.

As you can see by this terrible terrible interaction, the “database” isn’t actually doing anything until it’s ready to handle all the conditions. It’s lazy-loading results at each step and applying new conditions each time.

As opposed to getting the first 2000 users, returning them, filtering them for “Ab”, returning them, filtering them for over 20, returning them, and filtering for male and finally returning them.

Lazy loading in a nutshell.

3

Lazy evaluation of expressions will cause the designer of a given
piece of code lose control over the sequence their code is executed.

The designer shouldn’t care about the order in which expressions are evaluated provided the result is the same. By deferring evaluation, it may be possible to avoid evaluating some expressions altogether, which saves time.

You can see the same idea at work at a lower level: many microprocessors are able to execute instructions out of order, which lets them use their various execution units more efficiently. The key is that they look at dependencies between instructions and avoid reordering where it would change the result.

There are several arguments for lazy evaluation I think are compelling

  1. Modularity With lazy evaluation you can break code up into parts. For example, suppose you have the problem to “find the first ten reciprocals of elements in a list list such that the reciprocals are less than 1.” In something like Haskell you can write

    take 10 . filter (<1) . map (1/)
    

    but this is just incorrect in a strict language, since if you give it [2,3,4,5,6,7,8,9,10,11,12,0] you will be dividing by zero. See sacundim’s answer for why this is awesome in practice

  2. More things work Strictly (pun intended) more programs terminate with non strict evaluation than with strict evaluation. If your program terminates with an “eager” evaluation strategy it will terminate with a “lazy” one, but the oposite is not true. You get stuff like infinite data structures (which are really only kinda cool) as specific examples of this phenomenon. More programs work in lazy languages.

  3. Optimality Call-by-need evaluation is asymptotically optimal with respect to time. Although the major lazy languages (that essentially being Haskell and Haskell) don’t promise call-by-need, you can more or less expect an optimal cost model. Strictness analysers (and speculative evaluation) keep the overhead down in practice. Space is a more complicated matter.

  4. Forces Purity using lazy evaluation makes dealing with side effects in an undisciplined way a total pain, because as you put it, the programmer loses control. This is a good thing. Referential transparency makes programming, refractoring, and reasoning about programs so much easier. Strict languages just inevitably cave to the pressure of having impure bits–something Haskell and Clean have resisted beautifully. This is not to say that side effects are always evil, but controlling them is so useful that this reason alone is enough to use lazy languages.

Suppose you have a lot of expensive calculations on offer, but don’t know which ones will actually be needed, or in which order. You could add a complicated mother-may-i protocol
to force the consumer to figure out what’s available and trigger calculations that are not yet done. Or you could just provide an interface that acts as though the calculations were
all done.

Also, suppose you have an infinite result. The set of all primes for example. It’s obvious that you can’t calculate the set in advance, so any operation in the domain of primes has to be lazy.

with lazy evaluation you do not lose control about code execution, it is still absolutely deterministic. It is hard to get used with it, though.

lazy evaluation is useful because it is a way of reduction of lambda-term that will terminate in some cases, where eager evaluation will fail, but not vise versa. This includes
1) when you need to link to computation result before you actually execute computation, for example, when you constructs cyclic graph structure, but want to do it in functional style
2) when you define infinite data structure, but function this structure feed to use only part of datastructure.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật