7 Environments

7.1 Environment basics

  1. Q: List three ways in which an environment differs from a list.

    A: The most important differences between environments and lists are:
    • environments have reference semantics (i.e. they don’t copy-on-modify).
    • environments have parents
    • the contents of an environment are not ordered
    • the contents of an environment must have unique names
  2. Q: Create an environment as illustrated by this picture.

    A: Let’s create an environment, that contains itself.

  3. Q: Create a pair of environments as illustrated by this picture.

    A: These two environments contain each other:

  4. Q: Explain why e[[1]] and e[c("a", "b")] don’t make sense when e is an environment.

    A: The first option doesn’t make sense, because elements of an environment are not ordered. The second option would return two objects at the same time. What data structure would they be contained inside?

  5. Q: Create a version of env_poke() that will only bind new names, never re-bind old names. Some programming languages only do this, and are known as single assignment languages.

    A: We want env_poke2() to test, if the supplied name is already present in the given environment. We only allow new names to be assigned to a value, otherwise an (informative) error is thrown.

  6. Q: What does this function do? How does it differ from <<- and why might you prefer it?

    A: The primary differnce between rebind() and <<- is that rebind() will only carry out an assignment when it finds an existing binding; unlike <<- it will never create a new one in the global environment. This is usually undesirable, because global variables introduce non-obvious dependencies between functions.

7.2 Recursing over environments

  1. Q: Modify where() to return all environments that contain a binding for name. Carefully think through what type of object the function will need to return.

    A: The modified function will always recurse until it reaches the empty environment. Along the way, it will check each environment for a given name. Now it will return a list of environments where the binding was found; if no binding was found, the list will be empty. Please also note how the list is initialized via the default argument, when the function is called for the first time. This is a bit confusing, which is why it’s common to wrap a recurisve function inside another, more user friendly, function.

  2. Q: Write a function called fget() that finds only function objects. It should have two arguments, name and env, and should obey the regular scoping rules for functions: if there’s an object with a matching name that’s not a function, look in the parent. For an added challenge, also add an inherits argument which controls whether the function recurses up the parents or only looks in one environment.

    A: We follow a similar approach to the previous exercise. This time we additionally check if the found object is a function and implement and argument to turn off the recursion, if desired.

7.3 Special environments

  1. Q: How is search_envs() different fo env_parents(global_env())?

    A: search_envs() returns all the environments on the search path. “The search path is a chain of environments containing exported functions of attached packages” (from ?search_envs). Every time you attach a new package, this search path will grow. The search path ends with the base-environment. The global environment is included, because functions present in the global environment will always be part of the search path.

    env_parents(global_env()) will list all the ancestors of the global environment, therefore the global environment itself is not included. This also includes the “ultimate ancestor”, the empty environment. This environment is not considered part of the search path because it contains no objects.

  2. Q: Draw a diagram that shows the enclosing environments of this function:

    A: Each function environment binds its parent environment. The function environments contain functions and the values provided in the function call.

  3. Q: Write an enhanced version of str() that provides more information about functions. Show where the function was found and what environment it was defined in.

    A: To solve this problem, we need to write a function that takes the name of a function and looks for that function returning both the function and the environment that it was found in.

    Once you have learned about tidyeval, you could rewrite fstr() to use enquo() so that you’d call it like more like str(), i.e. fstr(sum).