RFP - Part4: R Variable Shadowing

Master R

By Guangming Lang Comment

Now we know that variables in R are objects, and that they can be manipulated, assigned any R objects and reassigned. (You can read the detailed discussion here.) We’ll discuss variable shadowing.

Variable reassignment in R

After we assign a R object to a variable, the variable will refer to that object. Let’s use a gray rectangular shape to represent the variable, and draw an arrow from the variable pointing to the object. For example, here is what’s going on behind the one-liner x = 3 pictorially: center

When a variable is reassigned another object, it won’t refer to the original object anymore. We represent this by changing the original solid arrow to a dashed one. A new solid arrow is drawn from the variable pointing to the new object. For example, the following diagram shows what happens if we reassign x to 32 (x = 32): center

The above diagrams depict how variable assignment and reassignment work in both R and Python. Before we talk about how variable shadowing works in R, let’s take a look at how it works in an old programing language called Standard ML (SML).

Variable shadowing in SML

“Variable assignment” works differently in SML. I’m putting “variable assignment” in quotes because SML programmers don’t call it that, instead, they call it “name binding.”1 For example, consider the same operation of first setting x to 3 and then to 32. Its SML code is

val x = 3;  (* bind x to 3 *)
val x = 32; (* bind x to 32 *)

The following diagram captures what happens when we run the SML code: center

The second x is a new variable that’s different from the first x! It shadows the first x. As a result, if we print x we’d get 32. So the mathematical reassignment operation is carried out by variable shadowing in SML.

Variable shadowing in R

R also has variable shadowing, which happens if the same variable is assigned in different scopes. Reassignment happens if the same variable is assigned twice or more times in the same scope. Consider the following R code, where each function definition creates a new scope:

x = 0 # global x
outer = function() {
        x = "The Great Gatsby" # outer x
        
        inner = function() {
                x = TRUE # inner x
                print(paste("inner:", x))
        }
        
        inner() # call function inner()
        print(paste("outer:", x)) 
}

{
        outer() # call function outer()
        print(paste("global:", x))
}
## [1] "inner: TRUE"
## [1] "outer: The Great Gatsby"
## [1] "global: 0"

From the above printed output, we see the x inside inner() shadows the x between inner() and outer(), which further shadows the x outside of outer(). This can be seen more clearly from the following diagram: center

  1. They make the distinction between “name binding” and “variable assignment.” To learn more, read https://masterr.org/r/RFP-part3-variables/. 

If you enjoyed this post, get updates. It's FREE