I have a few questions about the different environments of a function. Take the following example:
environment(sd)
#
TLDR:
Function environments
You have to distinguish 4 different environments when talking about a function:
find() gives you the binding environment.environment() gives you the enclosing environment.Why does this matter
Every environment has a specific function:
emptyenv().You can change the enclosing environment
Indeed, you can change the enclosing environment. It is the enclosing environment of a function from a package you cannot change. In that case you don't change the enclosing environment, you actually create a copy in the new environment:
> ls()
character(0)
> environment(sd)
> environment(sd) <- globalenv()
> environment(sd)
> ls()
[1] "sd"
> find("sd")
[1] ".GlobalEnv" "package:stats" # two functions sd now
> rm(sd)
> environment(sd)
In this case, the second sd has the global environment as the enclosing and binding environment, but the original sd is still found inside the package environment, and its enclosing environment is still the namespace of that package
The confusion might arise when you do the following:
> f <- sd
> environment(f)
> find("f")
[1] ".GlobalEnv"
What happens here? The enclosing environment is still the namespace ''stats''. That's where the function is created. However, the binding environment is now the global environment. That's where the name "f" is bound to the object.
We can change the enclosing environment to a new environment e. If you check now, the enclosing environment becomes e, but e itself is empty. f is still bound in the global environment.
> e <- new.env()
> e
> environment(f) <- e
> find("f")
[1] ".GlobalEnv"
> environment(f)
> ls(e)
character(0)
The enclosing environment of e is the global environment. So f still works as if its enclosure was the global environment. The environment e is enclosed in it, so if something isn't found in e, the function looks in the global environment and so on.
But because e is an environment, R calls that a parent environment.
> parent.env(e)
> f(1:3)
[1] 1
Namespaces and package environments
This principle is also the "trick" packages use:
The reason for this is simple: objects can only be found inside the environment you are in, or in its enclosing environments.
An illustration:
Now suppose you make an environment with the empty environment as a parent. If you use this as an enclosing environment for a function, nothing works any longer. Because now you circumvent all the package environments, so you can't find a single function any more.
> 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"
The parent frame
This is where it gets interesting. The parent frame or calling environment, is the environment where the values passed as arguments are looked up. But that parent frame can be the local environment of another function. In this case R looks first in that local environment of that other function, and then in the enclosing environment of the calling function, and so all the way up to the global environment, the environments of the attached packages until it reaches the empty environment. That's where the "object not found" bug sleeps.