R Force Local Scope

R scoping: disallow global variables in function

My other answer is more about what approach you can take inside your function. Now I'll provide some insight on what to do once your function is defined.

To ensure that your function is not using global variables when it shouldn't be, use the codetools package.

library(codetools)

sUm <- 10
f <- function(x, y) {
sum = x + y
return(sUm)
}

checkUsage(f)

This will print the message:

<anonymous> local variable ‘sum’ assigned but may not be used (:1)

To see if any global variables were used in your function, you can compare the output of the findGlobals() function with the variables in the global environment.

> findGlobals(f)
[1] "{" "+" "=" "return" "sUm"

> intersect(findGlobals(f), ls(envir=.GlobalEnv))
[1] "sUm"

That tells you that the global variable sUm was used inside f() when it probably shouldn't have been.

Creating scoped expressions in R to clean intermediate variables

local({foo <- 5; bar <- 6; foo + bar})

or

local({foo <- 5
bar <- 6
foo + bar})

from ?local (my emphasis)

local evaluates an expression in a local environment. It is equivalent to evalq except that its default argument creates a new, empty environment. This is useful to create anonymous recursive functions and as a kind of limited namespace feature since variables defined in the environment are not visible from the outside.

R scope: force variable substitution in function without local environment

Here are some approaches that use body<-

You could use bquote

handlers <- list()

for (i in 1:6) {
handlers[[paste0('h', i)]] <- function () {}
body( handlers[[paste0('h', i)]]) <- bquote(message(.(i)))
}

handlers$h1
## function ()
## message(1L)

or substitute

for (i in 1:6) {
handlers[[paste0('h', i)]] <- function () {}
body( handlers[[paste0('h', i)]]) <- substitute(message(i), list(i=i))
}

Force R function call to be self-sufficient

The problem here is, fundamentally, that library and similar tools don’t provide scoping, and are not designed to be made to work with scopes:1 Even though library is executed inside the function, its effect is actually global, not local. Ugh.

Specifically, your approach of isolating the function from the global environment is sounds; however, library manipulates the search path (via attach), and the function’s environment isn’t “notified” of this: it will still point to the previous second search path entry as its grandparent.

You need to find a way of updating the function environment’s grandparent environment when library/attach/… ist called. You could achieve this by replacing library etc. in the function’s parent environment with your own versions that calls a modified version of attach. This attach2 would then not only call the original attach but also relink your environment’s parent.


1 As an aside, ‘box’ fixes all of these problems. Replacing library(foo) with box::use(foo[...]) in your code makes it work. This is because modules are strongly scoped and environment-aware.

Global and local variables in R

Variables declared inside a function are local to that function. For instance:

foo <- function() {
bar <- 1
}
foo()
bar

gives the following error: Error: object 'bar' not found.

If you want to make bar a global variable, you should do:

foo <- function() {
bar <<- 1
}
foo()
bar

In this case bar is accessible from outside the function.

However, unlike C, C++ or many other languages, brackets do not determine the scope of variables. For instance, in the following code snippet:

if (x > 10) {
y <- 0
}
else {
y <- 1
}

y remains accessible after the if-else statement.

As you well say, you can also create nested environments. You can have a look at these two links for understanding how to use them:

  1. http://stat.ethz.ch/R-manual/R-devel/library/base/html/environment.html
  2. http://stat.ethz.ch/R-manual/R-devel/library/base/html/get.html

Here you have a small example:

test.env <- new.env()

assign('var', 100, envir=test.env)
# or simply
test.env$var <- 100

get('var') # var cannot be found since it is not defined in this environment
get('var', envir=test.env) # now it can be found

Scope of a variable inside a function in R

Here's a nice reprex to help demonstrate the role of environments in R as they apply to functions:

var <- 10

f <- function()
{
cat("Before function's var declared, var is", var, "\n")
var <- 5
cat("After function's var declared, var is", var, "\n")
this_functions_environment <- environment()
calling_environment <- parent.env(this_functions_environment)
cat("In the function's environment, var is", this_functions_environment$var)
cat("\nIn the calling environment, var is", calling_environment$var)
}

f()
#> Before function's var declared, var is 10
#> After function's var declared, var is 5
#> In the function's environment, var is 5
#> In the calling environment, var is 10

Created on 2020-04-28 by the reprex package (v0.3.0)

variable scope in R tryCatch block: is - necessary to change local variable defined before tryCatch?

The '<<-' is made for side effects that do not belong to R. Use it never, or only if memory or speed force you to do so. A block has it's own scope and if you want to give data from within a block to the 'outside' environement, that there is return() for that task:

test2 <- "a"

test2 <- tryCatch(stop(), error= function(err){
somevariable <- "b"
return(somevariable)
})

This makes it clear to everyone, that toplevel test2 is set to "a" and then, that toplevel test2 is set to something else. With '<<-' it happens easily, that some function changes toplevel test2 and someone wonders, why toplevel test2 has been changed at all. Just don't <<-.

If there is a need to return more than one result, return a list or an object of the results.

EDIT: The OP has pointed out that you need to be carefull with the return statements, as they do not only end the current block, but also the current function. A possible solution is, to run the computations in functions instead of simple blocks. The following example should illustrate this:

safediv <- function(a, b){
normalDo <- function(a, b){
return(list(value=a/b, message=NULL))
}
exceptionalDo <- function(err){
return(list(value=NaN, message="caught an error! Change global variable?"))
}
results <- tryCatch(normalDo(a, b), error=exceptionalDo)
print("safediv is still running after the returns within the functions.")
return(results)
}

# try it out
safediv(5, 3)
safediv(5, 0)
safediv(5, "a")


Related Topics



Leave a reply



Submit