Reading — step 1 of 5
Learn
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…