Part 6 · 1 chapters · ~8 min

Evaluation Strategies

Eager and lazy evaluation, call by value, by reference, by sharing and by name, short-circuiting, thunks and memoised laziness, generators and iterators, streams and backpressure, infinite sequences, and the costs of laziness (space leaks, unpredictable timing).

7

Eager, lazy and passing

code
// pass by sharing (JS, Python, Java)
function f(acct) { acct.balance = 0; acct = { balance: 999 }; }
const a = { balance: 100 }; f(a); a.balance   // 0: mutation visible, reassignment not

// laziness in an eager language: a generator yields on demand
function* ids() { let i = 0; while (true) yield `tr_${i++}`; }     // infinite, fine
const first3 = [...take(ids(), 3)];                                   // only 3 computed

// lazy logging: pass a thunk so the message is built only if needed
logger.debug(() => `state=${JSON.stringify(bigObject)}`);

-- Haskell: lazy by default
take 5 (filter even [1..])     -- [2,4,6,8,10] from an infinite list
-- cost: foldl (+) 0 [1..10^7] builds 10 million unevaluated thunks (a space leak); foldl' is strict
EVALUATION STRATEGIES
when arguments are computed, and how they are passed
eager (strict)args evaluated before the calllazyevaluated when first neededpass by valuecallee gets a copypass by sharingcopy of a referenceshort-circuit&&,
swipe the figure sideways, or tap expand for full screen
1/4
eager
Most languages evaluate arguments before calling: log(expensive()) computes expensive() even if logging is off.
compute firstmost languages