Avoid function name masking

rlang

#1

Is there a solution to the problem of function name masking in the following simple case:

x <- 1:5
foo <- function(x, fun) { fun(x) }
foo(x, fun=mean)

mean <- "bar"
foo(x, fun=mean)

"Advanced R"[1] by Hadley states: " If you are using a name in a context where it’s obvious that you want a function (e.g., f(3) ), R will ignore objects that are not functions while it is searching."
But actually, it does not seem to be the case here... NB: to be fair, adv-R also mentions that
"using the same name for functions and other objects [...] is generally best avoided." But in my use case the function is provided by the user and I cannot control what happens there.

I believe that, if there is a solution, it is in rlang. But there may actually not be a transparent one since, by the time object reaches the function, it is already a character string.

Thanks in advance for any pointer. Sincerely,

[1] http://adv-r.had.co.nz/Functions.html


#2

Interesting question!

I'm not entirely sure why your example behaves that way, but an example of this behavior can be shown like this:

x <- function () {
  "x()"
}

env <- new.env()
env$x <- "x"

do.call("x", list(), envir = env)

[1] "x()"

Regarding your main question, could you please clarify this a bit?

Which part of your snippet is given to you and which parts can you edit? Is just foo <- function(x, fun) { fun(x) } given to you? Is it given to you in a script or package, or do you have to copy paste it into another script like you've done here? Looking at your example, I'm wondering if you have a less synthetic example of your situation because I think the answer might depend on that. For example, you can execute a call to foo in another/parent environment than the one you've redefined/masked base::mean and avoid the masking. Or maybe something like this where I ask explicitly for base::mean would be workable for you?

x <- 1:5
foo <- function(x, fun) { fun(x) }
foo(x, fun=base::mean)

mean <- "bar"
foo(x, fun=base::mean)

#3

Thanks for the quick answer.

My full use case is: I wrote code that applies a function one in a moving window along a vector (https://github.com/jiho/castr/blob/master/R/slide.R)*. The function to be applied in the moving window is supplied by the user. I would like to make it as robust and simple as possible: allow the user to give a name, preferably unquoted, and find a function by that name somewhere.

Indeed, using base::mean solves the problem (because there is no masking anymore) but I'd like to avoid this if possible (i.e. make my code more complex for the user's code to be simple).

I am not sure this is completely possible. I tried to see how lapply() (and variants) do it and this is what I observe:

> x <- 1:5
> sapply(x, FUN=mean)
[1] 1 2 3 4 5
> mean <- "bar"
> sapply(x, FUN=mean)
Error in get(as.character(FUN), mode = "function", envir = envir) : 
  object 'bar' of mode 'function' was not found
> sapply(x, FUN="mean")
[1] 1 2 3 4 5
> sapply(x, FUN=base::mean)
[1] 1 2 3 4 5

So even l/sapply() gets confused there. I was wondering if, in my example above, some rlang magic would allow to get the name of the object passed to argument fun, in which case I would be able to use the get(name_passed_to_fun, mode="function")) trick that lapply() uses.


* If you read the code, you will see that my problem is actually more complicated than the example above because the user-supplied function is applied by an sapply() call within my function, and that my function is meant to be used within mutate(). But I suppose that if I can solve the simple problem above, then the rest should be solvable too.


#4

I seem to have found a solution. Not sure if it is the best one (seems like a bit of a hack) so I would welcome advice and/or examples where my solution fails. I'm using base functions currently. I'll consider rlang-based solutions if they are more elegant/robust, of course.

foo <- function(x, fun) {
  if (!is.function(fun)) {
    # if `fun` is not a function, try to get a function from the argument name itself
    fun <- get(as.character(substitute(fun)), mode="function")
  }
  fun(x)
}
foo(x, fun=mean)
mean <- "bar"
foo(x, fun=mean)
foo(x, fun=base::mean)
foo(x, fun="mean")

I'll implement this for now :wink:


#5

That seems like a pretty stable solution. Another thing I found while playing around with this was that do.call seems to do the lookup automatically so long as you pass the name of the function as a character vector,

> mean <- "bar"
> do.call(mean, list(1:5))
Error in bar(x = 1:5) : could not find function "bar"
> do.call("mean", list(1:5))
[1] 3

Not sure if that's helpful but I thought I'd share.