Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

R Language Dictionary

  1. Home
  2. R Language Dictionary
  3. Variable Scope and <<-

Variable Scope and <<-

In R, variables defined inside a function are confined to the local scope and do not affect the outside. To overwrite a variable in the parent environment, use the <<- operator (super-assignment). Explicitly manipulating environment objects also allows finer control over the scope mechanism.

Syntax

# -----------------------------------------------
# Local scope (<- creates a variable in the current function environment)
# -----------------------------------------------

outer_var <- "outer variable"

my_func <- function() {
  local_var <- "local variable"  # exists only in the function scope
  cat(local_var, "\n")
  cat(outer_var, "\n")           # can reference the parent environment's variable
}

my_func()
# cat(local_var, "\n")           # Error: local_var is not visible outside the function

# -----------------------------------------------
# Assigning to the parent environment with <<-
# -----------------------------------------------

counter <- 0

increment <- function() {
  counter <<- counter + 1     # directly modifies counter in the parent environment
}

increment()
increment()
cat(counter, "\n")             # prints 2

# -----------------------------------------------
# Environment manipulation with environment() and new.env()
# -----------------------------------------------

# Create a new environment
env <- new.env(parent = emptyenv())
assign("score", 100, envir = env)   # assign a variable to the env environment
cat(get("score", envir = env), "\n")  # retrieve the value from the env environment

Syntax / Function Reference

Syntax / FunctionDescription
x <- valueCreates or overwrites a variable in the current function scope (local environment).
x <<- valueTraverses parent environments and assigns to the first one where x is found. Creates in the global environment if not found anywhere.
environment()Returns the environment object in which the current function is executing. Called without arguments, returns the calling environment.
parent.env(env)Returns the parent environment of the specified environment.
new.env(parent = ...)Creates a new environment object. Specify the parent environment with parent.
assign(name, value, envir = ...)Assigns a variable to the specified environment. The key name can be passed as a string.
get(name, envir = ...)Retrieves the value of a variable from the specified environment.
ls(envir = ...)Returns a list of variable names existing in the specified environment.
exists(name, envir = ...)Returns TRUE / FALSE indicating whether a variable exists in the specified environment.
local({ ... })Evaluates expressions in a new local environment. Useful when you do not want temporary variables to leak outside.

Sample Code

kof_function_scope.R
# kof_function_scope.R — sample to explore variable scope and <<-
# Uses THE KING OF FIGHTERS character data
# to check local scope, parent environment assignment with <<-, and environment object manipulation

cat("=== THE KING OF FIGHTERS — Scope Verification ===\n\n")

# -----------------------------------------------
# Verifying local scope
# -----------------------------------------------

# Team name in the global environment
team_name <- "Fatal Fury Team"

# Creating a variable with the same name inside a function does NOT overwrite the global
check_scope <- function() {
  team_name <- "team name inside function"  # creates a local variable
  cat(sprintf("  team_name inside function: %s\n", team_name))
}

cat("--- Local scope ---\n")
check_scope()
# The global variable is unchanged after the function runs
cat(sprintf("  Global team_name: %s\n", team_name))
cat("\n")

# -----------------------------------------------
# Assigning to the parent environment with <<-
# -----------------------------------------------

# Track the number of entries with a global variable
entry_count <- 0

# A function that increments entry_count each time a fighter registers
register_fighter <- function(name) {
  entry_count <<- entry_count + 1    # update the global entry_count with <<-
  cat(sprintf("  [%d] %s has been registered.\n", entry_count, name))
}

cat("--- Assigning to the parent environment with <<- ---\n")
register_fighter("Kyo Kusanagi")
register_fighter("Iori Yagami")
register_fighter("Terry Bogard")
register_fighter("Kula Diamond")
register_fighter("Vanessa")
cat(sprintf("  Total registered fighters: %d\n", entry_count))
cat("\n")

# -----------------------------------------------
# Using <<- in a closure
# -----------------------------------------------

# Implement a win counter as a closure
make_win_counter <- function(fighter_name) {
  wins <- 0  # managed locally within the closure
  list(
    # add_win updates wins with <<- (references the make_win_counter environment)
    add_win = function() {
      wins <<- wins + 1
    },
    # get_wins returns the current win count
    get_wins = function() {
      cat(sprintf("  %s wins: %d\n", fighter_name, wins))
    }
  )
}

cat("--- Closure and <<- ---\n")

# Create a dedicated counter for each character
kyo_counter   <- make_win_counter("Kyo Kusanagi")
iori_counter  <- make_win_counter("Iori Yagami")
terry_counter <- make_win_counter("Terry Bogard")

# Record match results
kyo_counter$add_win()
kyo_counter$add_win()
kyo_counter$add_win()
iori_counter$add_win()
iori_counter$add_win()
terry_counter$add_win()

# Each counter is independent of the others
kyo_counter$get_wins()
iori_counter$get_wins()
terry_counter$get_wins()
cat("\n")

# -----------------------------------------------
# Environment manipulation with environment() and new.env()
# -----------------------------------------------

cat("--- environment() and new.env() ---\n")

# Create a dedicated environment object and store fighter data
fighter_env <- new.env(parent = emptyenv())

# Assign variables to the environment with assign()
assign("kyo",   list(name = "Kyo Kusanagi",  power = 92), envir = fighter_env)
assign("iori",  list(name = "Iori Yagami",   power = 88), envir = fighter_env)
assign("terry", list(name = "Terry Bogard",  power = 85), envir = fighter_env)

# Get a list of variable names in the environment with ls()
fighter_keys <- ls(envir = fighter_env)
cat(sprintf("  Keys registered in fighter_env: %s\n", paste(fighter_keys, collapse = ", ")))

# Retrieve values from the environment with get()
for (key in sort(fighter_keys)) {
  fighter <- get(key, envir = fighter_env)
  cat(sprintf("  %-6s -> %s (power: %d)\n", key, fighter$name, fighter$power))
}
cat("\n")

# -----------------------------------------------
# Temporary local environment with local()
# -----------------------------------------------

cat("--- Temporary scope with local() ---\n")

# Variables inside a local() block do not leak outside
final_result <- local({
  # tmp_scores exists only within this block
  tmp_scores <- c(
    kyo     = 95,
    iori    = 89,
    terry   = 82,
    kula    = 91,
    vanessa = 78
  )
  winner_name  <- names(which.max(tmp_scores))   # get the key with the highest score
  winner_score <- max(tmp_scores)
  sprintf("%s (score: %d)", winner_name, winner_score)  # the last value in the block is returned
})

cat(sprintf("  Tournament winner: %s\n", final_result))

# tmp_scores does not exist in the global environment
cat(sprintf("  Does tmp_scores exist: %s\n", exists("tmp_scores")))
cat("\n")
Rscript kof_function_scope.R
=== THE KING OF FIGHTERS — Scope Verification ===

--- Local scope ---
  team_name inside function: team name inside function
  Global team_name: Fatal Fury Team

--- Assigning to the parent environment with <<- ---
  [1] Kyo Kusanagi has been registered.
  [2] Iori Yagami has been registered.
  [3] Terry Bogard has been registered.
  [4] Kula Diamond has been registered.
  [5] Vanessa has been registered.
  Total registered fighters: 5

--- Closure and <<- ---
  Kyo Kusanagi wins: 3
  Iori Yagami wins: 2
  Terry Bogard wins: 1

--- environment() and new.env() ---
  Keys registered in fighter_env: iori, kyo, terry
  iori  -> Iori Yagami (power: 88)
  kyo   -> Kyo Kusanagi (power: 92)
  terry -> Terry Bogard (power: 85)

--- Temporary scope with local() ---
  Tournament winner: kyo (score: 95)
  Does tmp_scores exist: FALSE

Overview

R's variable scope is based on lexical scope (static scope), meaning a function remembers the environment in which it was defined. Using <- inside a function creates a variable confined to that local scope without affecting the outside. To overwrite an outer variable, use <<- (the super-assignment operator). <<- traverses from the current environment through parent environments and assigns to the first environment where the same-named variable is found. Combined with closures, this mechanism allows implementing counters or accumulation functions that retain state across calls. You can also explicitly create environment objects with new.env() and manipulate them with assign() and get(), which is useful for namespace isolation and dynamic key management. Using local() lets you group processing without leaking temporary variables into the global environment. Understanding scope deepens with Function Basics and Return Values.

Common Mistakes

Unintentionally overwriting a global variable with <<-

<<- assigns to the parent environment (usually the global environment). Overwriting a global variable inside a function introduces side effects that make debugging harder.

scope_superassign.R
counter <- 0

increment <- function() {
    counter <<- counter + 1  # overwrites the global counter
}

increment()
increment()
cat("Counter:", counter, "\n")  # 2
Counter: 2

When state management is needed, consider using return values or R5 classes (Reference Classes).

A local variable with the same name shadows the global variable

Assigning to a same-named variable inside a function creates a separate local variable rather than modifying the global one. The local variable takes precedence inside the function.

scope_shadow.R
name <- "Son Goku"

change_name <- function() {
    name <- "Vegeta"  # local variable (separate from the global)
    cat("Inside:", name, "\n")
}

change_name()
cat("Outside:", name, "\n")  # the global is unchanged
Inside: Vegeta
Outside: Son Goku

Loop variable capture in closures

When a closure is created inside a loop, the closure captures a "reference" to the loop variable rather than its "value". Calling the function after the loop ends means all closures reference the final value.

scope_closure.R
# Calling the closure immediately after creation gives the correct value
make_greeting <- function(name) {
    function() cat("Hello,", name, "!\n")
}

greetings <- lapply(c("Son Goku", "Vegeta", "Gohan"), make_greeting)

# Each closure captures its own name
greetings[[1]]()
greetings[[2]]()
greetings[[3]]()
Hello, Son Goku !
Hello, Vegeta !
Hello, Gohan !

If you find any errors or copyright issues, please .