rguides

do.call

do.call evaluates a function call constructed with call(). The primary use case is passing arguments stored in a list, programmatically constructing function calls, or dynamically dispatching to functions by name.

Signature

do.call(what, args, quote = FALSE, envir = parent.frame())

Arguments:

  • what, a function, or a character string naming a function to call
  • args, a list or pairlist of arguments to pass to the function
  • quote, if TRUE, args is treated as an unevaluated expression (Language object)
  • envir, the environment in which to evaluate the call

Returns: The result of calling what with the provided arguments.

How do.call relates to call()

call() constructs a call object without evaluating it. do.call() evaluates that call.

# call() builds the unevaluated expression
call("mean", x = 1:10)
# => mean(x = 1:10)

# do.call() evaluates it
do.call("mean", list(x = 1:10))
# => 5.5

call() alone does not run the function—it builds a Language object that represents an unevaluated expression. To actually fire the function, you pass that call to do.call(), which evaluates it in the specified environment and returns the result. This two-step process separates construction from execution, which is what makes metaprogramming possible.

Basic usage

Pass arguments as a named list. The function name can be a character string, which is useful when the function is chosen dynamically:

do.call("paste", list("a", "b", sep = "-"))
# => "a-b"

do.call() works with any R function, including built-ins, package functions, and user-defined closures without modification. The argument list can mix positional and named entries: unnamed elements fill parameters left to right, while named elements target specific formal arguments by name:

my_summary <- function(x, na.rm = TRUE) {
  c(mean = mean(x, na.rm = na.rm),
    sd = sd(x, na.rm = na.rm))
}

do.call(my_summary, list(1:10, na.rm = TRUE))
# => mean 5.5, sd 3.03

Constructing calls dynamically

Building arguments from a list

When your function arguments are stored in a list, do.call() unpacks each element as a separate argument. This is cleaner than manually indexing the list and passing each element positionally. For example, you can pass a list of numbers directly to an infix operator:

args <- list(1, 2, 3)
do.call("+", args)
# => 6

Using do.call with user functions

For user-defined functions, the argument list must use names that match the function’s formal parameters. When the names in your list match the parameter names exactly, do.call() routes each value to the correct argument regardless of position within the list:

calculate <- function(operation, x, y) {
  switch(operation,
    add = x + y,
    multiply = x * y)
}

do.call(calculate, list(operation = "add", x = 3, y = 4))
# => 7

Combining with match.call

A powerful metaprogramming pattern captures the call inside a wrapper function and replays it with do.call(). This technique is used by modelling functions like lm() and glm() that need to record the exact call for later inspection, or rebuild it with modified arguments before re-evaluating:

wrapper <- function(f, data, ...) {
  call_obj <- match.call()
  call_obj[[1]] <- as.name("do.call")
  call_obj$f <- as.name(f)
  eval(call_obj)
}

# This builds and runs do.call(f, list(data, ...))

do.call vs direct calling

When arguments are already available as individual values, a direct function call and do.call() produce identical results. The difference only matters when arguments are stored in a structured object like a list, where manual extraction would be tedious and error-prone:

# Direct call:
result <- mean(c(1, 2, 3))

# Equivalent with do.call:
result <- do.call(mean, list(c(1, 2, 3)))

do.call() becomes essential when arguments must be collected or manipulated programmatically before the function call. This happens frequently when working with split data: you gather groups into list elements and then pass them all at once to a combining function:

args <- split(mtcars$mpg, mtcars$cyl)
do.call(rbind, args)

Here rbind() gets called with all the list elements as separate arguments, something impossible to write directly without do.call.

Common use cases

Apply a function to split data

A frequent workflow splits a data frame by a grouping variable, applies a summary function to each group, and then recombines the results into a single data frame. do.call(rbind, ...) handles the recombination step, stacking each group’s summary row-by-row:

results <- do.call(rbind, lapply(split(mtcars, mtcars$gear), function(df) {
  data.frame(gear = df$gear[1], mean_mpg = mean(df$mpg))
}))

Dynamic function dispatch

When you need to select a function at runtime based on user input or configuration, do.call() lets you look up the function from a list and pass it arguments without hardcoding the function name. This pattern is common in shiny apps and pipeline runners where the user picks an operation from a dropdown:

operations <- list(
  mean = function(x) base::mean(x),
  median = function(x) stats::median(x)
)

op <- "median"
do.call(operations[[op]], list(1:10))
# => 5.5

Building a call from components

You can build both the function name and the argument list dynamically from variables. This pattern is useful when the function to call depends on runtime conditions and the arguments come from different sources that must be assembled into a single list:

func_name <- "t.test"
args <- list(x = rnorm(100), y = rnorm(100), var.equal = TRUE)
do.call(func_name, args)

do.call() is most useful when you have arguments stored in a list and want to pass them to a function programmatically. A common pattern is building a list of arguments conditionally, then calling a function with all of them at once. This avoids deeply nested if-else chains for optional arguments.

For very long argument lists, do.call() is also cleaner than match.arg() gymnastics. The tradeoff is that it can be harder to read and debug than a direct function call.

See also