Question

I'm pretty new to R, but coming from Scheme—which is also lexically scoped and has closures—I would expect being able to mutate outer variables in a closure.

E.g., in

foo <- function() {
  s <- 100

  add <- function() {
    s <- s + 1
  }

  add()
  s
}

cat(foo(), "\n") # prints 100 and not 101

I would expect foo() to return 101, but it actually returns 100:

$ Rscript foo.R
100

I know that Python has the global keyword to declare scope of variables (doesn't work with this example, though). Does R need something similar?

What am I doing wrong?

Update

Ah, is the problem that in add I am creating a new, local variable s that shadows the outer s? If so, how can I mutate s without creating a local variable?

Was it helpful?

Solution

Use the <<- operator for assignment in the add() function.

From ?"<<-":

The operators <<- and ->> are normally only used in functions, and cause a search to made through parent environments for an existing definition of the variable being assigned. If such a variable is found (and its binding is not locked) then its value is redefined, otherwise assignment takes place in the global environment. Note that their semantics differ from that in the S language, but are useful in conjunction with the scoping rules of R. See ‘The R Language Definition’ manual for further details and examples.

OTHER TIPS

You can also use assign and define the scope precisely using the envir argument, works the same way as <<- in your add function in this case but makes your intention a little more clear:

foo <- function() {
  s <- 100

  add <- function() {
   assign("s", s + 1, envir = parent.frame())
  }

  add()
  s
}

cat(foo(), "\n") 

Of course the better way for this kind of thing in R is to have your function return the variable (or variables) it modifies and explicitly reassigning them to the original variable:

foo <- function() {
  s <- 100
  add <- function(x) x + 1
  s <- add(s)
  s
}

cat(foo(), "\n") 

Here is one more approach that can be a little safer than the assign or <<- approaches:

foo <- function() {
    e <- environment()
    s <- 100

    add <- function() {
        e$s <- e$s + 1
    }

    add()
    s
}

foo()

The <<- assignment can cause problems if you accidentally misspell your variable name, it will still do something, but it will not be what you are expecting and can be hard to find the source of the problem. The assign approach can be tricky if you then want to move your add function to inside another function, or call it from another function. The best approach overall is to not have the functions modify variables outside their own scope and have the function return anything that is important. But when that is not possible, the above method uses lexical scoping to access the environment e, then assigns into the environment so it will always assign specifically into that function, never above or below.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top