Sergio Marrero Marrero
Sergio Marrero Marrero

Reputation: 178

Can someone explain the behaviour of `<<-` assignament operator in R?

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.

Upvotes: 0

Views: 69

Answers (1)

MrFlick
MrFlick

Reputation: 206566

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.

Upvotes: 4

Related Questions