Better way to deal with namespace when using glue::glue - r

I want to create a function that itself uses the awesome glue::glue function.
However, I came to find myself dealing with some namespace issue when I want to glue a variable that exists in both function and global environments:
x=1
my_glue <- function(x, ...) {
glue::glue(x, ...)
}
my_glue("foobar x={x}") #not the expected output
# foobar x=foobar x={x}
I'd rather keep the variable named x for package consistency.
I ended up doing something like this, which works pretty well so far but only postpone the problem (a lot, but still):
my_glue2 <- function(x, ...) {
x___=x; rm(x)
glue::glue(x___, ...)
}
my_glue2("foobar x={x}") #problem is gone!
# foobar x=1
my_glue2("foobar x={x___}") #very unlikely but still...
# foobar x=foobar x={x___}
Is there a better/cleaner way to do this?

Since the value x = 1 is nowhere passed to the function, in the current scenario a way to do this would be to evaluate the string in the global environment itself where the value of x is present before passing it to the function.
my_glue(glue::glue("foobar x={x}"))
#foobar x=1
my_glue(glue::glue("foobar x={x}"), " More text")
#foobar x=1 More text
Another option (and I think this is the answer that you are looking for) is to get the value of x from the parent environment. glue has .envir parameter, where the environment to evaluate the expression can be defined.
my_glue <- function(x, ...) {
glue::glue(x, ...,.envir = parent.frame())
}
my_glue("foobar x={x}")
#foobar x=1

Related

How to create and initialize a non existing variable to a default value in R?

This question originates from curiosity, I have nothing to deliver based on this.
Mimicking pass-by-reference (question here) I noticed that both approaches described in the answers obviously fail when the variable does not exist and one tries to use/reference them.
Regardless of its actual usefulness, I would be curious to know if there is a way to initialize the parameter x in the code below, and hence the "actual" parameter myVar, to a default value, with the help of the desired type passed as a string, xtype (passing the type, and in such basic form is not a requirement, it is simply the first thing that came to my mind of non-advanced R programmer).
The question whose solution generated this, here, shows better code in the chosen answer, here using my code as I understand it better
myF <- function(x, xtype) {
varName <- deparse(substitute(x))
if (!exists(varName)) {
# here should initialize x to a default value
# of the type passed in xtype
# to avoid that x <- x ... fails
# this may not have any practical usefulness, just curious
}
x <- x+1
assign(varName,x,envir=parent.frame(n = 1))
NA # sorry this is not a function
# in real life sometimes you also need procedures
}
if (exists(deparse(substitute(myVar)))) {
rm(myVar)
}
myF(myVar, "numeric")
print(myVar)
Error in myF(myVar, "numeric") : object 'myVar' not found
# as expected
Maybe this is what you are looking for (even though it's a terrible idea to write a function like this in R).
myF <- function(x, xtype) {
varName <- deparse(substitute(x))
if (!exists(varName)) {
x <- vector(xtype, 1)
} else {
x <- get(varName)
}
x <- x+1
assign(varName,x,envir=parent.frame(n = 1))
}

Why does my R function have knowledge of variables that are not given as arguments? [duplicate]

Is there any way to throw a warning (and fail..) if a global variable is used within a R function? I think that is much saver and prevents unintended behaviours...e.g.
sUm <- 10
sum <- function(x,y){
sum = x+y
return(sUm)
}
due to the "typo" in return the function will always return 10. Instead of returning the value of sUm it should fail.
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.
There is no way to permanently change how variables are resolved because that would break a lot of functions. The behavior you don't like is actually very useful in many cases.
If a variable is not found in a function, R will check the environment where the function was defined for such a variable. You can change this environment with the environment() function. For example
environment(sum) <- baseenv()
sum(4,5)
# Error in sum(4, 5) : object 'sUm' not found
This works because baseenv() points to the "base" environment which is empty. However, note that you don't have access to other functions with this method
myfun<-function(x,y) {x+y}
sum <- function(x,y){sum = myfun(x+y); return(sUm)}
environment(sum)<-baseenv()
sum(4,5)
# Error in sum(4, 5) : could not find function "myfun"
because in a functional language such as R, functions are just regular variables that are also scoped in the environment in which they are defined and would not be available in the base environment.
You would manually have to change the environment for each function you write. Again, there is no way to change this default behavior because many of the base R functions and functions defined in packages rely on this behavior.
Using get is a way:
sUm <- 10
sum <- function(x,y){
sum <- x+y
#with inherits = FALSE below the variable is only searched
#in the specified environment in the envir argument below
get('sUm', envir = environment(), inherits=FALSE)
}
Output:
> sum(1,6)
Error in get("sUm", envir = environment(), inherits = FALSE) :
object 'sUm' not found
Having the right sum in the get function would still only look inside the function's environment for the variable, meaning that if there were two variables, one inside the function and one in the global environment with the same name, the function would always look for the variable inside the function's environment and never at the global environment:
sum <- 10
sum2 <- function(x,y){
sum <- x+y
get('sum', envir = environment(), inherits=FALSE)
}
> sum2(1,7)
[1] 8
You can check whether the variable's name appears in the list of global variables. Note that this is imperfect if the global variable in question has the same name as an argument to your function.
if (deparse(substitute(var)) %in% ls(envir=.GlobalEnv))
stop("Do not use a global variable!")
The stop() function will halt execution of the function and display the given error message.
Another way (or style) is to keep all global variables in a special environment:
with( globals <- new.env(), {
# here define all "global variables"
sUm <- 10
mEan <- 5
})
# or add a variable by using $
globals$another_one <- 42
Then the function won't be able to get them:
sum <- function(x,y){
sum = x+y
return(sUm)
}
sum(1,2)
# Error in sum(1, 2) : object 'sUm' not found
But you can always use them with globals$:
globals$sUm
[1] 10
To manage the discipline, you can check if there is any global variable (except functions) outside of globals:
setdiff(ls(), union(lsf.str(), "globals")))

R: make R *not* search for variable in higher environments [duplicate]

I'm trying to write a function, which limits the scope of R variables. For example,
source("LimitScope.R")
y = 0
f = function(){
#Raises an error as y is a global variable
x = y
}
I thought of testing the variable environment, but wasn't really sure of how to do this.
The why
I teach R to undergrads. In their first couple of practicals a few of them always forget about variable scope, so their submitted functions don't work. For example, I always get something like:
n = 10
f = function(x){
#Raises an error
#as I just source f and test it for a few test cases.
return(x*n)
}
I was after a quick function that would 'turn off' scope. As you can imagine it doesn't have to be particularly robust, as it would just be offered for the few practicals.
I'm not sure that you want to do this in general, but the local() function should help, as should the codetools library.
In your example, try
f = local( function() { ... }, baseenv())
It does not do exactly what you want, but it should get you closer.
You can force a variable to be the local version with this function:
get_local <- function(variable)
{
get(variable, envir = parent.frame(), inherits = FALSE)
}
Compare these cases
y <- 0
f <- function()
{
x <- y
}
print(f()) # 0
y <- 0
f <- function()
{
y <- get_local("y")
x <- y
}
print(f()) # Error: 'y' not found
Depending on what you are doing, you may also want to check to see if y was an argument to f, using formalArgs or formals.
g <- function(x, y = TRUE, z = c("foo", "bar"), ...) 0
formalArgs(g)
# [1] "x" "y" "z" "..."
formals(g)
#$x
#
#
#$y
#[1] TRUE
#
#$z
#c("foo", "bar")
#
#$...
EDIT: The more general point of 'how to turn off lexical scoping without changing the contents of functions' is harder to solve. I'm fairly certain that the scoping rules are pretty ingrained into R. An alternative might be to use S-Plus, since it has different scoping rules.
You can check if y exists in the global environment using exists('y',envir=.GlobalEnv)
What occassionally happens to me is that I've got a split screen in ESS with a file buffer of R code on the left and the interpreter on the right. I may have set some values in the interpreter while I debug the code I am working on in the buffer. It's then possible that the code in the buffer accidentally refereneces something I set in the intepreter. That's hard to detect problem unless I evaluate my buffer in a fresh interpreter every time, which is not how ESS works by default.
If this is the kind of problem you are seeing frequently, an rm(list=ls(envir=.GlobalEnv)) in the thing you source might help, but that of course creates other issues, such as erasing anything they were using to keep state while debugging, etc.

How to retrieve formals of a primitive function?

For the moment, at least, this is an exercise in learning for me, so the actual functions or their complexity is not the issue. Suppose I write a function whose argument list includes some input variables and a function name, passed as a string. This function then calculates some variables internally and "decides" how to feed them to the function name I've passed in.
For nonprimitive functions, I can do (for this example, assume non of my funcname functions have any arguments other than at most (x,y,z). If they did, I'd have to write some code to search for matching names(formals(get(funcname))) so as not to delete the other arguments):
foo <- function (a,b,funcname) {
x <- 2*a
y <- a+3*b
z <- -b
formals(get(funcname)) <- list(x=x, y=y, z=z)
bar <- get(funcname)()
return(bar)
}
And the nice thing is, even if the function funcname will execute without error even if it doesn't use x, y or z (so long as there are no other args that don't have defaults) .
The problem with "primitive" functions is I don't know any way to find or modify their formals. Other than writing a wrapper, e.g. foosin <-function(x) sin(x), is there a way to set up my foo function to work with both primitive and nonprimitive function names as input arguments?
formals(args(FUN)) can be used to get the formals of a primitive function.
You could add an if statement to your existing function.
> formals(sum)
# NULL
> foo2 <- function(x) {
if(is.primitive(x)) formals(args(x)) else formals(x)
## formals(if(is.primitive(x)) args(x) else x) is another option
}
> foo2(sum)
# $...
#
#
# $na.rm
# [1] FALSE
#
> foo2(with)
# $data
#
#
# $expr
#
#
# $...
Building on Richard S' response, I ended up doing the following. Posted just in case anyone else ever tries do things as weird as I do.
EDIT: I think more type-checking needs to be done. It's possible that coleqn could be
the name of an object, in which case get(coleqn) will return some data. Probably I need
to add a if(is.function(rab)) right after the if(!is.null(rab)). (Of course, given that I wrote the function for my own needs, if I was stupid enough to pass an object, I deserve what I get :-) ).
# "coleqn" is the input argument, which is a string that could be either a function
# name or an expression.
rab<-tryCatch(get(coleqn),error=function(x) {} )
#oops, rab can easily be neither NULL nor a closure. Damn.
if(!is.null(rab)) {
# I believe this means it must be a function
# thanks to Richard Scriven of SO for this fix to handle primitives
# we are not allowed to redefine primitive's formals.
qq <- list(x=x,y=y,z=z)
# matchup the actual formals names
# by building a list of valid arguments to pass to do.call
argk<-NULL
argnames<-names(formals(args(coleqn)))
for(j in 1:length(argnames) )
argk[j]<-which(names(qq)==argnames[1] )
arglist<-list()
for(j in 1:length(qq) )
if(!is.na(argk[j])) arglist[[names(qq)[j]]]<-qq[[j]]
colvar<- do.call(coleqn,arglist)
} else {
# the input is just an expression (string), not a function
colvar <- eval(parse(text=coleqn))
}
The result is an object generated either by the expression or the function just created, using variables internal to the main function (which is not shown in this snippet)

Limiting variable scope

I'm trying to write a function, which limits the scope of R variables. For example,
source("LimitScope.R")
y = 0
f = function(){
#Raises an error as y is a global variable
x = y
}
I thought of testing the variable environment, but wasn't really sure of how to do this.
The why
I teach R to undergrads. In their first couple of practicals a few of them always forget about variable scope, so their submitted functions don't work. For example, I always get something like:
n = 10
f = function(x){
#Raises an error
#as I just source f and test it for a few test cases.
return(x*n)
}
I was after a quick function that would 'turn off' scope. As you can imagine it doesn't have to be particularly robust, as it would just be offered for the few practicals.
I'm not sure that you want to do this in general, but the local() function should help, as should the codetools library.
In your example, try
f = local( function() { ... }, baseenv())
It does not do exactly what you want, but it should get you closer.
You can force a variable to be the local version with this function:
get_local <- function(variable)
{
get(variable, envir = parent.frame(), inherits = FALSE)
}
Compare these cases
y <- 0
f <- function()
{
x <- y
}
print(f()) # 0
y <- 0
f <- function()
{
y <- get_local("y")
x <- y
}
print(f()) # Error: 'y' not found
Depending on what you are doing, you may also want to check to see if y was an argument to f, using formalArgs or formals.
g <- function(x, y = TRUE, z = c("foo", "bar"), ...) 0
formalArgs(g)
# [1] "x" "y" "z" "..."
formals(g)
#$x
#
#
#$y
#[1] TRUE
#
#$z
#c("foo", "bar")
#
#$...
EDIT: The more general point of 'how to turn off lexical scoping without changing the contents of functions' is harder to solve. I'm fairly certain that the scoping rules are pretty ingrained into R. An alternative might be to use S-Plus, since it has different scoping rules.
You can check if y exists in the global environment using exists('y',envir=.GlobalEnv)
What occassionally happens to me is that I've got a split screen in ESS with a file buffer of R code on the left and the interpreter on the right. I may have set some values in the interpreter while I debug the code I am working on in the buffer. It's then possible that the code in the buffer accidentally refereneces something I set in the intepreter. That's hard to detect problem unless I evaluate my buffer in a fresh interpreter every time, which is not how ESS works by default.
If this is the kind of problem you are seeing frequently, an rm(list=ls(envir=.GlobalEnv)) in the thing you source might help, but that of course creates other issues, such as erasing anything they were using to keep state while debugging, etc.

Resources