environment()
An environment in R is a data structure that associates (binds) names to objects. Unlike lists, environments have a parent environment that forms a scope chain — when R looks up a name, it searches the current environment first, then walks up the parent chain until it finds the name or reaches the empty environment. This is how R’s lexical scoping works.
Creating Environments
You can create new environments with new.env(). By default, the parent is the current environment (the global environment in interactive use).
# Create a new empty environment
env <- new.env()
# Add variables to it using $ or [[]]
env$x <- 10
env[["y"]] <- "hello"
# Access variables
env$x
# [1] 10
Working with Environments
| Function | Description |
|---|---|
ls(env) | List all names in an environment |
get("x", envir = env) | Retrieve a value by name |
assign("x", value, envir = env) | Set a value by name |
exists("x", envir = env) | Check if a name exists |
rm("x", envir = env) | Remove a binding |
parent.env(env) | Get the parent environment |
environmentName(env) | Get the environment’s name |
env <- new.env()
env$a <- 1:5
env$b <- "data"
ls(env)
# [1] "a" "b"
exists("a", envir = env)
# [1] TRUE
get("b", envir = env)
# [1] "data"
The Scope Chain
Every environment has a parent. When you use a name, R searches up the chain:
# Create parent environment
parent <- new.env()
parent$shared <- "I'm in parent"
# Create child environment with parent
child <- new.env(parent = parent)
child$local <- "I'm in child"
child$shared
# [1] "I'm in parent"
child$local
# [1] "I'm in child"
Common Patterns
Package Namespaces
Packages use environments to isolate their code. When you load a package with library(), R attaches it to the search path:
# The base environment is at the top of the search path
search()
# [1] ".GlobalEnv" "package:stats"
# [3] "package:base"
# dplyr creates its own environment with its functions
library(dplyr)
ls(where = as.environment("package:dplyr"))[1:5]
# [1] "%>%" "filter" "select" "mutate" "arrange"
Function Environments
Every function has an associated environment where its free variables are looked up:
make_adder <- function(x) {
function(y) x + y
}
add5 <- make_adder(5)
environment(add5)
# <environment: 0x558a2b6e8>
# x is stored in the function's enclosing environment
get("x", envir = environment(add5))
# [1] 5
Caching with Environments
Environments are mutable and use reference semantics, making them useful for caching:
cache <- new.env()
compute_expensive <- function(key) {
if (exists(key, envir = cache)) {
message("Fetching from cache")
return(get(key, envir = cache))
}
result <- Sys.sleep(1) # simulate expensive computation
result <- paste0("computed: ", key)
assign(key, result, envir = cache)
result
}
compute_expensive("data1") # Takes ~1 second
# [1] "computed: data1"
compute_expensive("data1") # Instant (from cache)
# Fetching from cache
# [1] "computed: data1"