5

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?

csl
  • 10,937
  • 5
  • 57
  • 89
  • Moderators can close as duplicates or you can join in and vote to have a membership closure. – IRTFM Apr 26 '14 at 16:23
  • @BondedDust Gotcha, I've voted for this to be closed as well. Just had problems Googling for it --- I actually didn't find the solution until I posted here. So other mods may decide what to do with it. – csl Apr 26 '14 at 16:25
  • 1
    Two more votes and it will be closed regardless of any moderator attention. You may find that the Reference Classes facility is a paradigm that fits with your programming style and would not be seen as dangerous by regular R uses. It uses named environments and lets you have some of the features of "global" variables. – IRTFM Apr 26 '14 at 16:28
  • Thanks. I will stand by my vote to close it. Thank you everyone! – csl Apr 26 '14 at 16:30

3 Answers3

3

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.

gagolews
  • 12,836
  • 2
  • 50
  • 75
  • You say "for assignment". Should I *always* use `<<-` for assignment? Only use `<-` for declarations? – csl Apr 26 '14 at 16:21
  • 1
    Only if you wish to access a name binding outside the function (informally speaking). Otherwise you shouldn't use it at all: it's quite "dangerous". – gagolews Apr 26 '14 at 16:22
  • It says that `<<-` will assign in the _global_ environment if it can't find any references going up the parent environments. Sounds scary to me, I would expect it to define it _locally_ instead. Is this a problem? – csl Apr 26 '14 at 16:23
  • 3
    Most experienced users of R would be horrified to see you adopting the strategy of always using `<<-` for assignments. You _should_ be assigning the value of the function to a name in the workspace. – IRTFM Apr 26 '14 at 16:25
  • 2
    Here, it will find the binding in `foo()`, so it's OK. Otherwise, it may really be creepy, thus it's usage is discouraged (unless you know what you do). – gagolews Apr 26 '14 at 16:25
3

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") 
sebkopf
  • 2,335
  • 19
  • 18
2

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.

Greg Snow
  • 48,497
  • 6
  • 83
  • 110