5 Environments

5.1 Environment basics

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

    A: The most important differences are:
    • environments have reference semantics
    • environments have parents
    • environments are not ordered
    • elements of environments need to be (uniquely) named
  2. Q: Create an environment as illustrated by this picture.

    A:

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

    A:

  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.

    TODO: 2nd part of the question…

  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:

5.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: We look at the source code of the original pryr::where():

    Since where() stops searching when a match appears, we copy the recursive call in the else block to the block of the matching (“success”) case, so that our new function where2 will look for a binding within the complete search path. We also need to pay attention to other details. We have to take care to save the bindings in an object, while not overriding it in our recursive calls. So we create a list object for that and define a new function within where2() that we call where2.internal. where2.internal() will do the recursive work and whenever it finds a binding it will write it via <<- to the especially created list in its enclosing environment:

    Note that where2.internal() still provides the same structure as pryr::where does and you can also divide it in “base case”, “success case” and “recursive case”.

    TODO: Second part of the question.

  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 can build up our function on the implementation of get2() in the last exercise. We only need to add a check via is.function(), change the name (also in the recursive call) and the error message:

    Note that this function is almost the same as the implementation of pryr::fget():

    We add an inherits parameter as described in the exercise:

5.3 Special environments

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

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

    A: TODO: Adopt the diagram to Hadley’s new diagram-style in the 2nd-edition chapter.

  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: Additionally we provide the function type in the sense of pryr::ftype. We use functions from the pryr package, since it provides helpers for all requested features:

    Note that we wanted to have non standard evaluation like the original str() function. Since pryr::where() doesn’t support non standard evaluation, we needed to catch the name of the supplied object. Therefore we used expr_text() from the lazyeval package. As a result, fstr(object = packagename::functionname) will result in an error in contrast to str().

5.4 The call stack

  1. Q: Write a function that lists all the variables defined in the environment in which it was called. It should return the same results as ls(). A:

5.5 <<-

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

    A: The function does “more or less” the same as <<-. Additionally to <<- it has an env argument, but this is not a big advantage, since also assign() provides this functionality. The main difference is that rebind() only does an assignment, when it finds a binding in one of the parent environments of env. Whereas:

    If <<- doesn’t find an existing variable, it will create one in the global environment. This is usually undesirable, because global variables introduce non-obvious dependencies between functions.