Skip to content
Lesson 1 of 7

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 chainglobalenv(), 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
Up nextVectorization TricksEnvironments and Scoping

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…