Environments, Scoping & Closures

An environment maps names to values and has a parent.

23.1 Environments

An environment maps names to values and has a parent.

Environments have reference semantics (not copied on modify) — used for mutable state, caches, R6 objects.

e <- new.env()
assign("x", 10, envir = e); get("x", envir = e); e$y <- 5
ls(e); exists("x", envir = e); rm("x", envir = e)
environment()          # current environment
globalenv(); emptyenv(); baseenv()
parent.env(globalenv())
search()               # the search path: .GlobalEnv, attached packages, base
environmentName(environment(mean))   # "base"

23.2 Lexical scoping

R looks up names where a function was defined, not where it's called:

Lookup order: current function environment → enclosing (defining) environments → global → packages on the search path → base → empty.

x <- "global"
f <- function() { x <- "f"; g <- function() x; g() }
f()     # "f"

23.3 Closures & function factories

make_counter <- function() {
  count <- 0
  function() {
    count <<- count + 1    # modify variable in the enclosing environment
    count
  }
}
counter <- make_counter()
counter(); counter()       # 1, 2

power <- function(exp) { force(exp); function(x) x^exp }
square <- power(2); cube <- power(3)
square(4); cube(2)

23.4 Dynamic lookup & caller environments

f <- function() parent.frame()   # caller's environment
sys.call(); match.call(); sys.function()
local({ tmp <- 1; tmp + 1 })     # evaluate in a temporary environment