RUBY / CHAPTER 12

Lazy sequences and controlled failure

Advanced

Keep large traversals bounded and cleanup predictable.

By the end of this chapter

  • Distinguish eager and lazy enumeration
  • Bound infinite or large sequences before materializing
  • Recover from expected failures without suppressing defects

The idea

Ordinary collection transformations commonly build their result eagerly. Enumerator::Lazy can defer chained operations until a consumer requests values, allowing a pipeline to stop after enough matches are found. Laziness is especially useful with an unbounded sequence, where materializing everything is impossible. Place a finite bound such as take before converting such a sequence into an array.

Delayed execution also means an error may occur during consumption rather than when the pipeline is defined. Rescue only failures for which the caller has a meaningful policy, and keep cleanup dependable with block-based resource APIs such as File.open or with ensure. Do not rescue every exception to return an apparently valid empty collection. Test early termination and failures as well as the ordinary complete traversal.

Ruby example
values = (1..).lazy.select { |n| n % 3 == 0 }.map { |n| n * 2 }.take(3)
p values.force
Result
[6, 12, 18]

Only enough positive integers are inspected to produce three matching doubled values. The take bound makes materialization finite.

Try it yourself

Produce the first five nonblank normalized lines from a lazy source, and explain where a source failure would be observed.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

Where should take(5) appear when materializing an unbounded lazy sequence?