Scope — Where R Looks Up a Name
Your per_area() function uses a variable called area that it never received as an argument, and it still works. Then, inside another function, you set area <- 100, and per_area() ignores it completely. Both behaviours follow from one rule about where R looks for names.
The rule
R looks inside the function first, then where the function was defined.
Every call to a function gets a fresh, private environment: a little workspace for its arguments and any variables it creates. When the body uses a name, R searches:
- the function's own environment,
- then the environment where the function was written (for most code, the global workspace),
- and outwards from there, ending with loaded packages and base R.
The first match wins.
area <- 4 # global
per_area <- function(kg) {
kg / area # area is not local, so R finds the global 4
}
per_area(12.5) # 3.125
per_area_5 <- function(kg) {
area <- 5 # a local area, only inside this call
kg / area
}
per_area_5(12.5) # 2.5
area # still 4Assignment with <- inside a function always creates or changes a local variable. It never touches the global one, which is why area is still 4 afterwards.
Because the environment is fresh on every call, nothing is remembered between calls:
counter <- function() {
n <- 0
n <- n + 1
n
}
counter(); counter() # 1, then 1 againTo keep state on purpose, a function can build and return another function. The inner one remembers the environment it was created in, and <<- assigns to a variable in that enclosing environment instead of making a local one:
make_counter <- function() {
n <- 0
function() {
n <<- n + 1
n
}
}
tick <- make_counter()
tick(); tick() # 1, then 2Use <<- sparingly. A function that quietly changes variables outside itself is hard to reason about.
The shortcut
Draw nested boxes. A function's box sits inside the box where it was written, not where it happened to be called. To find a name, search from the innermost box outwards; to assign with <-, write in the innermost box.
The setup
area <- 4,f <- function() areaandg <- function() { area <- 100; f() }, all written at the top level.
Drills
Answer fast — trust the rule.
After area <- 4, h <- function() { area <- 9; area } and h(), what is area at the top level?
With the setup from the walk-through, what does g() return?
Why does counter() return 1 every time?
After tick <- make_counter(), what does the third call to tick() return?
Remember
- R looks inside the function first, then where the function was defined.
- Shortcut: nested boxes by where code was written; search outwards, assign in the innermost box.
- Trap: expecting a caller's local variable to be visible, and overusing
<<-.
