Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Functions  ›  Lesson

Scope — Where R Looks Up a Name

Scopes 12 minR finds a variable by searching the function's own environment first and then the environment where the function was defined, and ordinary assignment inside a function only creates local variables.

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:

  1. the function's own environment,
  2. then the environment where the function was written (for most code, the global workspace),
  3. 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 4

Assignment 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 again

To 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 2

Use <<- 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.

Why does g() return 4?Step 1 of 4
  1. The setup

    area <- 4, f <- function() area and g <- function() { area <- 100; f() }, all written at the top level.

Drills

Answer fast — trust the rule.

quick drill

After area <- 4, h <- function() { area <- 9; area } and h(), what is area at the top level?

quick drill

With the setup from the walk-through, what does g() return?

quick drill

Why does counter() return 1 every time?

quick drill

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 <<-.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainDefined here, called there

With area <- 4, f <- function() area and g <- function() { area <- 100; f() }, explain why g() returns 4 and not 100, and what area is at the top level afterwards.