renvironment-variables

R fn_env with a string argument to get the environment of a function


I would like to use R's fn_env() with a string argument, for example my_fn_env("count"). The users of the program can enter any function, and may also include functions with the :: notation, e.g., "plyr::count", so that this function should return the same as fn_env(plyr::count)

I considered this:

do.call("fn_env", list("plyr::count"), quote=FALSE)

but the quoted string given as an argument remains quoted. Chapter 7 of the e-book "Advanced R" (here) has a function where() which returns the environment when a match is found, accepting strings, but it does not accept the :: notation, apparently.

Any suggestions?


Solution

  • Sure, we can write a function that does that! First, split the input string on ::

    It then uses get() or getExportedValue() to retrieve the actual function. Finally, it returns the environment of that function.

    This should give you the same results as using fn_env() directly on the function, but with the flexibility of accepting string inputs.

    using<-function(...) {
      libs<-unlist(list(...))
      req<-unlist(lapply(libs,require,character.only=TRUE))
      need<-libs[req==FALSE]
      if(length(need)>0){ 
        install.packages(need)
        lapply(need,require,character.only=TRUE)
      }
    }
    using("rlang")
    
    my_fn_env <- function(fun_name) {
      # Split the string in case it contains namespace specification
      parts <- strsplit(fun_name, "::")[[1]]
      
      if (length(parts) == 1) {
        # Case 1: Regular function name without namespace
        fn <- tryCatch(
          get(fun_name, mode = "function"),
          error = function(e) stop("Function '", fun_name, "' not found")
        )
        return(environment(fn))
      } else if (length(parts) == 2) {
        # Case 2: Function with namespace specification
        pkg <- parts[1]
        fun <- parts[2]
        
        # Check if package is installed and loaded
        if (!requireNamespace(pkg, quietly = TRUE)) {
          stop("Package '", pkg, "' is not installed")
        }
        
        # Get the function from the namespace
        fn <- tryCatch(
          getExportedValue(pkg, fun),
          error = function(e) stop("Function '", fun, "' not found in package '", pkg, "'")
        )
        return(environment(fn))
      } else {
        stop("Invalid function name format")
      }
    }
    
    # normal way using fn_env
    f <- function(x) x + y
    fn_env(f)
    
    # Test our function!
    my_fn_env("f") 
    
    my_fn_env("mean") 
    my_fn_env("plyr::count")