5. Lexical Scoping

Closures & Variable Capture

seniorcompiler-internalsdata-structures

A closure's defining property: it captures the environment where it was DEFINED, not wherever it happens to be CALLED from. This exercise builds a tiny expression interpreter and asks you to implement exactly that rule for function calls -- get it wrong and a function's free variables end up depending on the caller instead of staying fixed at definition time.

Node shapes:

  • ("num", value) -- an integer literal
  • ("var", name) -- look up name in the current environment
  • ("add", left, right) -- left + right
  • ("lambda", param, body) -- a one-argument function literal
  • ("call", func_expr, arg_expr) -- call a function with one argument

"num"/"var"/"add"/"lambda" are done for you (note that evaluating a "lambda" captures the CURRENT env into a Closure right there). Implement the "call" case in eval_expr(node, env):

  1. Evaluate func_expr in the current env to get a Closure.
  2. Evaluate arg_expr in the current (calling) env.
  3. Evaluate the closure's body in a NEW environment built from the closure's CAPTURED env (closure.env, not the calling env) plus the argument bound to closure.param.

Example

# (lambda x: x + 1)(5)
ast = ("call", ("lambda", "x", ("add", ("var", "x"), ("num", 1))), ("num", 5))
eval_expr(ast, {})  # -> 6

Constraints

  • The property that matters: a closure must see the variables it captured at definition time, even when called from an environment where those same variable names mean something different.

Real-world grounding

Every language with first-class functions and lexical scoping (Python, JavaScript, Rust closures, Go's function literals) implements this exact rule -- a closure carries its defining environment along with it. CPython's version of this uses "cell" objects to share captured variables between the defining scope and the closure: Objects/funcobject.c / Objects/cellobject.c

hints
closures.pysign in to run
Loading...
test result

This is a preview. Sign in to run this exercise against the real tests.