0

After some hours I found a bug in my code because an unexpected behaviour of the <<- assinament operator in r. I have read the documentation and browsed on internet but I still do not understand the behaviour of the operator.

Look these two functions:

# Define a function a_counter
a_counter  <- function(){

  i <<- i + 1
  print(i)

}
> i <- 0
> a_counter()
[1] 1
> print(i)
[1] 1
> a_counter()
[1] 2
> print(i)
[1] 2
# Define a function not_a_counter
not_a_counter  <- function(){

  i <- 0
  i <<- i + 1
  print(i)

}
> i <- 0
> not_a_counter()
[1] 0
> print(i)
[1] 1
> not_a_counter()
[1] 0
> print(i)
[1] 1

The first chunk of code runs as I expected, the variable i in both environments (function env. and global env.) are increased in each function call.

The second chunk of code is absolutely unexpected for me. The i <<- i + 1 does not assign the value to the i located in the function environment, but it does so in the i located in the global environment. I expected both enviroments to be updated.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • 2
    Your `a_counter` function, run in a clean R session, will return an error. Did you leave out a line somewhere? You probably forgot that you had already defined `i` in your global environment. I'm guessing you copy & pasted something wrong, since the function names don't match in subsequent code. – joran Apr 17 '19 at 16:49
  • 2
    To be honest, I think you've understood and explained it pretty well. There's another question here that has a bunch of good answers, see https://stackoverflow.com/q/2628621/210673. You question is actually likely a duplicate, but before I flag it as such, perhaps read that one and those answers and see what questions you might still have. – Aaron left Stack Overflow Apr 17 '19 at 16:52
  • Thanks for your comment. I think the question you mention is too open for any kind of answer. I would like to keep this question, as here I asking for something very concrete. Anyway I will come across all the [answers](stackoverflow.com/q/2628621/210673) and find out something useful. – Sergio Marrero Marrero Apr 17 '19 at 17:01

1 Answers1

4

In your a_counter, there is only one value of i. Not two. When a "free variable" is found in a function, it's looked up in the environment in which the function was defined. So when you call i in that function, it goes up to the global environment to find the value. Then when you do <<-, assignment doesn't happen in the function environment at all. <<- always starts looking in the parent environment. If you look at

counter_vars  <- function() {
  a <- 4 
  i <<- i + 1
  ls() 
}
counter_vars()
# [1] "a"

you'll see that the only variable inside the function environment/closure is the a variable. The i variable doesn't exist there. So with the original function all the is come from global scope

a_counter  <- function(){    
  i <<- i + 1    # global i = global i + 1
  print(i)       # global i still (no local variable has been created)
}

So the behavior you see in not_a_counter should be expected because <<- will not change values in current environment. It always start looking one environment "up". When you have

not_a_counter  <- function(){    
  i <- 0         # local i (always 0)
  i <<- i + 1    # global i == local i + 1 (always 0+1)
  print(i)       # local i (always still 0, local value not changed)
}

The i variable is no longer "free" once you define it in the function. So i <- 0 creates a local variable, in the i <<- i + 1 part, the right hand side i+1 uses that local variable and assigns to the i in the parent environment.

MrFlick
  • 195,160
  • 17
  • 277
  • 295