Most languages you use day-to-day — Python, TypeScript, Scala, Java — evaluate arguments before passing them to a function. This is eager evaluation (also: strict evaluation). A few languages — Haskell, Clojure’s lazy-seq, Racket’s delay — defer evaluation until the value is demanded: lazy evaluation.
The difference matters as soon as you care about: (a) avoiding unnecessary work, (b) short-circuit logic, (c) infinite structures (coming in Lesson 29).
Terms:
- Eager / strict evaluation: an argument expression is evaluated before the function call. By the time the function body runs, all argument values are computed.
- Lazy / non-strict evaluation: an argument expression is evaluated when and only when its value is demanded — possibly never.
- Thunk: an explicit encoding of a lazy value in an eager language — a zero-argument lambda:
lambda: <expr>. Creating the thunk is cheap; the expression runs only when you callthunk().
Part 1 — Predict: what does always_first do?
def always_first(x, y):
return x
always_first(42, 1 / 0) # ?
In Python (eager): what happens, and when does it happen? (Before the function body runs, or inside it?)
If Python were lazy: what would happen instead?
Part 2 — Manual thunking
Python gives you an escape hatch: wrap the risky argument in a lambda.
def always_first_lazy(x, y_thunk):
return x # never calls y_thunk()
always_first_lazy(42, lambda: 1 / 0) # ?
Why does wrapping in lambda: prevent the error?
Part 3 — Short-circuit as hidden laziness
Python’s and and or are not regular functions:
False and (1 / 0) # → False, no error
True or (1 / 0) # → True, no error
But a custom my_and function isn’t lazy:
def my_and(a, b):
if a: return b
return False
my_and(False, 1 / 0) # ?
What happens, and why does and (the built-in operator) succeed where my_and fails? Fix my_and using a thunk so it short-circuits correctly.
Part 4 — Lazy vs eager on a non-terminating argument
def f(x):
return 1
f(loop_forever()) # loop_forever() never returns
In an eager language: what happens when you call f(loop_forever())?
In a lazy language: what happens?
The question above
The MCQ at the top of this puzzle asks about evaluation order — a specific, testable consequence of Python’s eager semantics. Think it through before reading the solution.
After answering, confirm your mental model against Parts 1–4.