Eager Versus Lazy Evaluation
Eager evaluation computes a value when defined; lazy evaluation defers until needed, so lazy avoids wasted work but adds per-access thunk overhead and complicates reasoning about when effects run.
Definition
Eager versus lazy evaluation is the choice of when a computation runs — eager computes at definition, paying the cost up front and storing the result; lazy defers until the value is forced, avoiding wasted work when the value is never used but adding a thunk indirection on every access and making the order of effects harder to reason about.
Context and scope
Applied in functional languages, infinite data structures, and any setting where the cost of computing an unused value or the benefit of short-circuiting justifies the indirection.
Common misconceptions
- Lazy is always more efficient — it is more efficient when the value is unused; when always used, lazy adds overhead and eager wins
- Lazy evaluation is just deferred function calls — it is deferred with memoization and a specific forcing discipline; a deferred call alone is not lazy evaluation
Anti-patterns
- Making every computation lazy in a language that does not default to it, adding thunk overhead everywhere for values that are always forced
- Using lazy evaluation for side-effecting computations, so the side effect runs at an unpredictable force time rather than the definition site
Related concepts
Authoritative source
What it is
Eager versus lazy evaluation is the choice of when a computation runs — eager computes at definition, paying the cost up front and storing the result; lazy defers until the value is forced, avoiding wasted work when the value is never used but adding a thunk indirection on every access and making the order of effects harder to reason about. Applied in functional languages, infinite data structures, and any setting where the cost of computing an unused value or the benefit of short-circuiting justifies the indirection. Performance is a distribution, not a number: the tail determines user experience, and the mean hides the outliers that cause abandonment.
How it works
Measurement targets percentiles at the workload’s actual concurrency, not a single sequential run. The fast path is kept fast by deferring work that is not on it — lazy initialization, coalescing writes, avoiding allocation in hot loops. Profiling finds the bottleneck; micro-optimization without a profile optimizes the wrong thing. Constant factors matter, but algorithmic complexity dominates as scale grows.
When to optimize and pitfalls
Optimize when a measurement shows a concrete cost — latency budget burn, compute spend, user-visible jank. The pitfall is optimizing for the benchmark rather than the workload: a synthetic test that does not reproduce the production access pattern produces a local maximum that hurts globally. Another is trading memory for speed without accounting for the GC or eviction cost the extra allocation introduces.