ProgrammingiOS Developer

What is a lazy sequence in Swift? How does it work, when should it be used, and what are the pitfalls?

Pass interviews with Hintsage AI assistant

Answer.

Lazy sequence is a special wrapper around a collection that allows deferring the execution of computations (filter, map, etc.) until actual access to the elements occurs. This means that operations on the lazy sequence will only be computed when their result is accessed (e.g., when calling .forEach, .first, converting to an array, etc.).

When to use:

  • When working with large sequences where there's no need to process all elements at once.
  • When wanting to avoid intermediate allocations during sequential transformations (map -> filter -> ...).

Example:

let numbers = Array(1...1_000_000) let lazyNumbers = numbers.lazy.map { $0 * 2 }.filter { $0 % 3 == 0 } let first = lazyNumbers.first // The chain of operations will only be computed now for the first matching element!

Pitfalls:

  • Computations are re-evaluated each time when iterating over the .lazy sequence.
  • They depend on the order of calls: if multiple passes over the data are needed with different conditions — the data will be recalculated.
  • Deferred actions can be a source of unexpected side effects if there are side effects in the nested closures.

Trick question.

What is the difference between calling .map { ... } and .lazy.map { ... } on an array?

Answer:

  • .map { ... } applies the closure to each element and immediately returns a new array, meaning all elements will be processed and stored in memory.
  • .lazy.map { ... } returns not an array, but a lazy sequence (wrapper), which does not process elements immediately but only upon access.

Example:

let a = Array(1...10) let eagers = a.map { $0 * 2 } // Array with 10 elements let laziers = a.lazy.map { $0 * 2 } // LazySequence, not containing results immediately

Examples of real errors due to ignorance of the topic's nuances.


Story

In a large project, a developer applied a chain of several calls to map, filter, reduce on a huge array of data without .lazy. This led to temporarily allocating large arrays at each intermediate step, nearly doubling memory consumption and causing crashes on some low-RAM devices.


Story

A lazy sequence with a side effect in the inner closure (e.g., sending an event or printing inside map/filter) was used in a code block. The developer expected this operation to execute immediately, but the event did not happen at all — because the elements of the lazy sequence were never accessed, and the code with the event was not invoked at all. As a result, logs and metrics were inaccurate.


Story

In the case of collecting statistics from data in a large database, a lazy sequence was used in combination with multiple passes (e.g., looking for the first element twice, and then counting). Each pass over the lazy sequence initiated a full recalculation of operations — leading to a twofold slowdown and unnecessary strain on the system. The problem disappeared after switching to a regular array.