TL; DR:
- indeed, you can change the environment. Hadley probably talked about packed features.
- environment and binding environment. You were right.
- what is the execution environment. It exists only for the execution time of a function.
Functional environments
When discussing a function, you should distinguish 4 different environments:
- the binding environment is the environment in which the function is located (i.e. where its name exists). This is where the actual binding of the object to its name is performed.
find() provides you with a binding environment. - Environment is the environment in which the function was originally created. This is not necessarily the same as the binding environment (see Examples below).
environment() provides the environment. - the local environment is the environment inside the function. You call it a runtime.
- the parent frame or calling environment is the environment from which the function is called.
Why does it matter
Each environment has a specific function:
- The binding environment is the environment in which you will find this feature.
- the local environment is the first environment where R searches for objects.
- general rule: if R does not find the object in the local environment, it looks in the environment and so on. The last environment is always
emptyenv() . - the parent frame is where R searches for the value of the objects passed as arguments.
You can change the environment
In fact, you can change the environment. This is an environment of a function from a package that you cannot change. In this case, you do not change the environment, you actually create a copy in the new environment:
> ls() character(0) > environment(sd) <environment: namespace:stats> > environment(sd) <- globalenv() > environment(sd) <environment: R_GlobalEnv> > ls() [1] "sd" > find("sd") [1] ".GlobalEnv" "package:stats" # two functions sd now > rm(sd) > environment(sd) <environment: namespace:stats>
In this case, the second sd has a global environment as an inclusion and binding environment, but the original sd is still inside the package environment, and the environment is still the namespace of this package
Confusion may occur if you do the following:
> f <- sd > environment(f) <environment: namespace:stats> > find("f") [1] ".GlobalEnv"
What's going on here? The environment is still the "statistics" of the namespace. This is where the function is created. However, the binding environment is now a global environment. Where the name "f" is attached to the object.
We can change the environment to a new environment e . If you check now, the environment will become e , but e itself is empty. f is still connected in a global environment.
> e <- new.env() > e <environment: 0x000000001852e0a8> > environment(f) <- e > find("f") [1] ".GlobalEnv" > environment(f) <environment: 0x000000001852e0a8> > ls(e) character(0)
Environment e is a global environment. Thus, f still works as if its shell were a global environment. It contains the e environment, so if something is not found in e , the function looks into the global environment and so on.
But since e is the environment, R calls the parent environment.
> parent.env(e) <environment: R_GlobalEnv> > f(1:3) [1] 1
Namespaces and Package Environments
This principle is also used in trick packages:
- a function is created in the namespace. This is an environment that is surrounded by the namespaces of other imported packages and, ultimately, the global environment.
- the binding for the function is created in the package environment. This is an environment that covers a global environment and possible other packages.
The reason for this is simple: objects can only be found inside the environment in which you are located, or in environments.
- a function must be able to find other functions (objects), so the local environment should be enclosed, possibly in the namespaces of other packages it imports, the base package, and finally, the global environment.
- the function must be found from the global environment. Therefore, the binding (i.e., the name of the function) must be in an environment that is surrounded by a global environment. This is a package environment (NOT a namespace!)
Illustration:

Now suppose you are creating an environment with an empty environment as the parent. If you use this as the environment for a function, nothing works. Because now you have bypassed all the package environments, so you can no longer find one function.
> orphan <- new.env(parent = emptyenv()) > environment(f) <- orphan > f(1:3) Error in sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x), : could not find function "sqrt"
Parent frame
Here it becomes interesting. A parent frame or calling environment is an environment in which values โโpassed as arguments are viewed. But this parent frame may be the local environment of another function. In this case, R first looks in this local environment of this other function, and then in the environment of the calling function and, thus, up to the global environment, the environment of attached packets, until it reaches an empty environment. That the failure "object not found".