Step 1 of 5 · Reading · ~1 min
Learn
Environments and Scoping
In R, everything has an environment. Functions, the global namespace, even names you bind. Understanding environments is key to closures, packages, and metaprogramming.
Create and inspect:
e <- new.env()
assign("x", 5, envir = e)
get("x", envir = e) # 5
ls(envir = e) # "x"
rm("x", envir = e)
Or use $:
e$x <- 5
e$x # 5
Function environments — every function captures its enclosing environment:
make_counter <- function() {
count <- 0
function() {
count <<- count + 1 # <<- modifies enclosing scope
count
}
}
c1 <- make_counter()
c1() # 1
c1() # 2
environment(c1) # the env where count lives
The <<- operator searches outward from the function's enclosing environment for the name. If found, modifies it. If not, creates in global. Use cautiously.
Environment lookup chain — globalenv(), baseenv(), package envs:
find("mean") # "package:base"
environment(mean) # base namespace
When R looks up a name, it walks: current function env → enclosing → ... → global → loaded packages → base.
Closures share environments:
make_account <- function(balance) {
deposit <- function(n) balance <<- balance + n
withdraw <- function(n) balance <<- balance - n
get_balance <- function() balance
list(deposit = deposit, withdraw = withdraw, balance = get_balance)
}
a <- make_account(100)
a$deposit(50)
a$withdraw(20)
a$balance() # 130
All three closures share the balance upvalue.
local({...}) for clean private scope:
computed <- local({
x <- 5
y <- 10
x + y # last expression
})
# computed = 15
# x and y are NOT in the global scope
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…