Search code examples
rellipsisexpression-evaluation

function to load multiple packages with input as non-character list


I got tired of starting my scripts with require(data.table); require(ggplot2) etc etc. I know that I could do something like lapply(c('data.table', 'ggplot2'), require, character.only=T), but it would just return a warning (not an error) for a missing package, plus I wanted my own function require_mult(...) to keep my scripts cleaner. So I wrote the function:

require_mult <- function(...){
  arg_list <- list(...)
  if(any(!sapply(arg_list, is.character)) ) stop('require_mult must have character arguments')
    check <- tryCatch({pkg_array <- unlist(arg_list)
                       loaded <- sapply(pkg_array, require, character.only=TRUE, quietly=TRUE)
                       message('Loaded packages: ',
                               paste(pkg_array, collapse=', ') ) },
                       warning = function(w) stop(w), # I want execution to halt if a package is missing
                       error = function(e) stop(e)
                       )
}

Which seems to work as desired (e.g., loads for require_mult('data.table','ggplot2'), errors for require_mult('data.table','ggplotfoo')), but I would like to make this require_mult(...) work for character.only=FALSE. I looked at the definition of require, which uses

if (!character.only) 
        package <- as.character(substitute(package))

But I can't figure out how to apply substitute across an unevaluated expression that's really a comma-delimited list.

I know this application is pretty pointless and academic, but it would enrich my understanding of R expression evaluation if there is in fact a way to write a require_mult that could be used for require_mult(data.table, ggplot2) etc.

An explanation of why this can't be done would also be acceptable.


Solution

  • I learned something here today! You can use match.call to obtain the arguments passed in ... without evaluating them. (Presumably, you could do this with named arguments...? I need to experiment more on that.) I used this answer to build the very simple function below. As you can see, it breaks when reaching the package that does not exist.

    I think you could use this to build further.

    library_mult <- function(...) {
        args <- match.call(expand.dots = FALSE)$`...`
        for(x in args) {
    
            x <- as.character(substitute(x))
            library(x,character.only = TRUE)
        }
    }
    
    > library_mult(MASS,thisisnopack)
    
    Error in library(x, character.only = TRUE) : 
      there is no package called ‘thisisnopack’