Here is the rule, and it is stricter than Python's:
Module variables are invisible inside functions. A function sees its parameters, its own locals, other functions, structs, and imported vocabulary — nothing else. Data enters through parameters and leaves through the return value.
count = 5
fn bad[]
return count + 1 --- error: 'count' is a module variable, which a
--- function does not see — pass it as a parameter
The fix is always the same shape:
1 2 3 4 5 6
count = 5
fn bump[n]
return n + 1
log[bump[count]]expected output — from the book, verified by crow test --book
6A local that happens to reuse a module name shadows nothing — module
variables were never visible, so it's simply a fresh binding. No global
keyword, no nonlocal, no scope-resolution table to memorize.
Two reasons, and they're both load-bearing:
crow check all get harder.
Parameters-in, return-out means every dependency is visible at the
call site.The one sanctioned exception is reactive state in web UIs (view blocks
and event handlers, chapter 12) — a managed channel, synchronized by the
runtime, not ambient mutation.