Skip to content
AgileLogics
6 min read

Environments, Scoping, and Closures

Prerequisites: Scope and anonymous functions

An environment is where R stores variables: a set of names bound to values, plus a link to a parent environment. Understanding environments explains how R finds variables — and enables powerful patterns like closures.

How R looks up a name

When you use a name, R searches the current environment, then its parent, then the parent's parent, and so on up to the global environment and the loaded packages. This is lexical scoping: a function looks up names in the environment where it was defined, not where it's called.

multiplier <- 10

scale <- function(x) x * multiplier

run <- function() {
  multiplier <- 1000
  scale(5)
}

run()
[1] 50

scale was defined in the global environment, so it finds the global multiplier (10) — not the one inside run.

Closures — functions that remember

A function created inside another function keeps access to the environment it was created in:

make_counter <- function() {
  count <- 0
  function() {
    count <<- count + 1
    count
  }
}

counter <- make_counter()
counter()
counter()
counter()
[1] 1
[1] 2
[1] 3
  • count lives in the environment created by make_counter(). The returned function keeps that environment alive.
  • <<- assigns to a variable in a parent environment instead of creating a local one.

Closures power function factories — functions that build functions:

power <- function(exponent) {
  function(x) x^exponent
}

square <- power(2)
cube <- power(3)
square(4)
cube(2)
[1] 16
[1] 8

Environments as objects

e <- new.env()
assign("total", 42, envir = e)
get("total", envir = e)
exists("total")
exists("total", envir = e)
[1] 42
[1] FALSE
[1] TRUE

Unlike lists, environments are modified in place (reference semantics) — which is how R6 classes (a later lesson) work.

Common mistakes

  • Using <<- to change global variables from inside functions. It makes code hard to follow and test. Return values instead; keep <<- for deliberate closures.
  • Functions that silently depend on global variables. If a function needs a value, pass it as an argument.