v3 Semantics: Nested Functions
- Resolve nested-function references through lexical scopes.
- Distinguish known declaration arity from dynamically stored callable values.
- Preserve const and declaration-order rules without inventing type inference.
1 / 6
Analyze let base=3; fn make(x){ fn add(y){ return base+x+y; } return add; }.
Register base, then register make before traversing its body. Push a function
scope with parameter x. Register add in that scope before entering its body;
inside add, y resolves locally, x resolves in make's scope, and base resolves
in the outer scope. Return to make's scope, resolve add as its return value,
then pop the function scope.
Moving base's declaration after make changes the result: base is unavailable when make's body is analyzed. There is no global hoisting pass. Self-recursion is allowed because the current function binding is installed before its body. Function bindings are const-like; assigning to their name is rejected.
An analyzer rejects every f() where f was introduced with let, yet accepts const a=[1]; a=[2];. Explain why its call policy is too strict and its assignment policy too weak.
Write one nested function whose outer reference is valid, then move the declaration past the use to produce a name error. Compare direct known-function wrong arity with wrong arity through a variable holding that function.
Carry a name-valid AST into closure compilation. Record which decisions remain dynamic so later runtime failures are not misdiagnosed as missing static checks.