
Last chance! 50% off unlimited learning
Sale ends in
env()
and child_env()
create new environments. env()
always
creates a child of the current environment while child_env()
lets
you specify a parent (see section on inheritance).
env(...)child_env(parent, ...)
Named values. These dots have explicit splicing semantics.
A parent environment. Can be an object supported by
as_env()
.
Environments are containers of uniquely named objects. Their most common use is to provide a scope for the evaluation of R expressions. Not all languages have first class environments, i.e. can manipulate scope as regular objects. Reification of scope is one of the most powerful feature of R as it allows you to change what objects a function or expression sees when it is evaluated.
Environments also constitute a data structure in their own right. They are dictionaries of uniquely named objects, subsettable by name and modifiable by reference. This latter property (see section on reference semantics) is especially useful for creating mutable OO systems (cf the R6package and the ggprotosystem for extending ggplot2).
All R environments (except the empty environment) are defined with a parent environment. An environment and its grandparents thus form a linear hierarchy that is the basis for lexicalscoping in R. When R evaluates an expression, it looks up symbols in a given environment. If it cannot find these symbols there, it keeps looking them up in parent environments. This way, objects defined in child environments have precedence over objects defined in parent environments.
The ability of overriding specific definitions is used in the
tidyeval framework to create powerful domain-specific grammars. A
common use of overscoping is to put data frame columns in
scope. See as_overscope()
for technical details.
Unlike regular objects such as vectors, environments are an
uncopyable object type. This means that if you
have multiple references to a given environment (by assigning the
environment to another symbol with <-
or passing the environment
as argument to a function), modifying the bindings of one of those
references changes all other references as well.
scoped_env
, env_has()
, env_bind()
.
# NOT RUN {
# env() creates a new environment which has the current environment
# as parent
env <- env(a = 1, b = "foo")
env$b
identical(env_parent(env), get_env())
# child_env() lets you specify a parent:
child <- child_env(env, c = "bar")
identical(env_parent(child), env)
# This child environment owns `c` but inherits `a` and `b` from `env`:
env_has(child, c("a", "b", "c", "d"))
env_has(child, c("a", "b", "c", "d"), inherit = TRUE)
# `parent` is passed to as_env() to provide handy shortcuts. Pass a
# string to create a child of a package environment:
child_env("rlang")
env_parent(child_env("rlang"))
# Or `NULL` to create a child of the empty environment:
child_env(NULL)
env_parent(child_env(NULL))
# The base package environment is often a good default choice for a
# parent environment because it contains all standard base
# functions. Also note that it will never inherit from other loaded
# package environments since R keeps the base package at the tail
# of the search path:
base_child <- child_env("base")
env_has(base_child, c("lapply", "("), inherit = TRUE)
# On the other hand, a child of the empty environment doesn't even
# see a definition for `(`
empty_child <- child_env(NULL)
env_has(empty_child, c("lapply", "("), inherit = TRUE)
# Note that all other package environments inherit from base_env()
# as well:
rlang_child <- child_env("rlang")
env_has(rlang_child, "env", inherit = TRUE) # rlang function
env_has(rlang_child, "lapply", inherit = TRUE) # base function
# Both env() and child_env() take dots with explicit splicing:
objs <- list(b = "foo", c = "bar")
env <- env(a = 1, !!! objs)
env$c
# You can also unquote names with the definition operator `:=`
var <- "a"
env <- env(!!var := "A")
env$a
# }
Run the code above in your browser using DataLab