lazy
Defer module execution until the result is actually needed.
Quick Reference
| Parameter | Type | Description |
|---|---|---|
lazy | Boolean | Defer execution until result needed |
Syntax:
result = ExpensiveModule(input) with lazy: true
Syntax
result = Module(args) with lazy
# or
result = Module(args) with lazy: true
Type: Boolean (flag or explicit true/false)
Description
The lazy option defers the execution of a module until its result is actually used. The deferred computation is wrapped in a LazyValue that executes on first access and memoizes the result for subsequent accesses.
This is useful for expensive computations that may not always be needed, or for breaking circular dependencies.
With lazy evaluation, errors happen when the lazy value is first accessed, not when defined. This can make debugging harder since the stack trace points to the access site, not the definition. Add logging at access points for complex lazy chains.
Once a lazy value is forced, the result is cached for all subsequent accesses. The module executes exactly once, even if accessed from multiple places. This includes errors: if the first access fails, subsequent accesses will re-throw the same error.
Examples
Defer Expensive Computation
in shouldProcess: Boolean
in data: Record
# Only computed if shouldProcess is true
expensive = HeavyComputation(data) with lazy
output = when shouldProcess then expensive else defaultValue
out output
The HeavyComputation only runs if shouldProcess is true.
Explicit Boolean Value
# Enable lazy evaluation
deferred = Compute(x) with lazy: true
# Disable (execute immediately - default behavior)
immediate = Compute(x) with lazy: false
Multiple Access (Memoization)
cached = ExpensiveLoad(id) with lazy
# First access triggers execution
first = process(cached)
# Second access uses memoized result
second = transform(cached)
out { first: first, second: second }
The module only executes once, and both uses get the same result.
Conditional Branching
in useNewAlgorithm: Boolean
# Both computations are defined but only one runs
oldResult = OldAlgorithm(data) with lazy
newResult = NewAlgorithm(data) with lazy
output = when useNewAlgorithm then newResult else oldResult
out output
Behavior
- When a lazy module call is encountered:
- Create a
LazyValuewrapper containing the computation - Return the wrapper immediately (no execution)
- Create a
- When the lazy value is first accessed:
- Execute the wrapped computation
- Store the result (memoize)
- Return the result
- On subsequent accesses:
- Return the memoized result (no re-execution)
Thread Safety
Lazy evaluation is thread-safe:
- Only one thread executes the computation
- Other threads wait for the result
- All threads see the same memoized value
Related Options
When to Use Lazy
Good use cases:
- Expensive computations that may not be needed
- Conditional branches where only one path executes
- Breaking circular dependencies
- Deferring I/O until necessary
Avoid when:
- The value is always needed (no benefit)
- Timing of execution matters (unpredictable)
- You need explicit error handling at definition time
Best Practices
- Use lazy for expensive operations in conditional paths
- Remember that errors occur when the value is forced, not defined
- Combine with timeout to handle slow lazy evaluations
- Consider the memoization behavior when side effects matter