R force local scope

Solution 1:

As far as I know, R does not provide a "use strict" mode. So you are left with two options:

1 - Ensure all your "strict" functions don't have globalenv as environment. You could define a nice wrapper function for this, but the simplest is to call local:

# Use "local" directly to control the function environment
f <- local( function(myvar) { return(myVar); }, as.environment(2))
f(3) # Error in f(3) : object 'myVar' not found

# Create a wrapper function "strict" to do it for you...
strict <- function(f, pos=2) eval(substitute(f), as.environment(pos))
f <- strict( function(myvar) { return(myVar); } )
f(3) # Error in f(3) : object 'myVar' not found

2 - Do a code analysis that warns you of "bad" usage.

Here's a function checkStrict that hopefully does what you want. It uses the excellent codetools package.

# Checks a function for use of global variables
# Returns TRUE if ok, FALSE if globals were found.
checkStrict <- function(f, silent=FALSE) {
    vars <- codetools::findGlobals(f)
    found <- !vapply(vars, exists, logical(1), envir=as.environment(2))
    if (!silent && any(found)) {
        warning("global variables used: ", paste(names(found)[found], collapse=', '))
        return(invisible(FALSE))
    }

    !any(found)
}

And trying it out:

> myVar = 1
> f <- function(myvar) { return(myVar); }
> checkStrict(f)
Warning message:
In checkStrict(f) : global variables used: myVar

Solution 2:

checkUsage in the codetools package is helpful, but doesn't get you all the way there. In a clean session where myVar is not defined,

f <- function(myvar) { return(myVar); }
codetools::checkUsage(f)

gives

<anonymous>: no visible binding for global variable ‘myVar’

but once you define myVar, checkUsage is happy.

See ?codetools in the codetools package: it's possible that something there is useful:

> findGlobals(f)
[1] "{"      "myVar"  "return"
> findLocals(f)
character(0)