list2() is equivalent to list(...) but provides tidy dots semantics:

  • You can splice other lists with the unquote-splice !!! operator.

  • You can unquote names by using the unquote operator !! on the left-hand side of :=.

We call quasiquotation support in dots tidy dots semantics and functions taking dots with list2() tidy dots functions. Quasiquotation is an alternative to do.call() idioms and gives the users of your functions an uniform syntax to supply a variable number of arguments or a variable name.

dots_list() is a lower-level version of list2() that offers additional parameters for dots capture.

dots_list(..., .ignore_empty = c("trailing", "none", "all"),
  .preserve_empty = FALSE, .homonyms = c("keep", "first", "last",
  "error"), .check_assign = FALSE)

list2(...)

Arguments

...

Arguments to collect with !!! support.

.ignore_empty

Whether to ignore empty arguments. Can be one of "trailing", "none", "all". If "trailing", only the last argument is ignored if it is empty.

.preserve_empty

Whether to preserve the empty arguments that were not ignored. If TRUE, empty arguments are stored with missing_arg() values. If FALSE (the default) an error is thrown when an empty argument is detected.

.homonyms

How to treat arguments with the same name. The default, "keep", preserves these arguments. Set .homonyms to "first" to only keep the first occurrences, to "last" to keep the last occurrences, and to "error" to raise an informative error and indicate what arguments have duplicated names.

.check_assign

Whether to check for <- calls passed in dots. When TRUE and a <- call is detected, a warning is issued to advise users to use = if they meant to match a function parameter, or wrap the <- call in braces otherwise. This ensures assignments are explicit.

Value

A list of arguments. This list is always named: unnamed arguments are named with the empty string "".

Details

Note that while all tidy eval quoting functions have tidy dots semantics, not all tidy dots functions are quoting functions. list2() is for standard functions, not quoting functions.

Life cycle

One difference of dots_list() with list2() is that it always allocates a vector of names even if no names were supplied. In this case, the names are all empty "". This is for consistency with enquos() and enexprs() but can be quite costly when long lists are spliced in the results. For this reason we plan to parameterise this behaviour with a .named argument and possibly change the default. list2() does not have this issue.

See also

exprs() for extracting dots without evaluation.

Examples

# Let's create a function that takes a variable number of arguments: numeric <- function(...) { dots <- list2(...) num <- as.numeric(dots) set_names(num, names(dots)) } numeric(1, 2, 3)
#> [1] 1 2 3
# The main difference with list(...) is that list2(...) enables # the `!!!` syntax to splice lists: x <- list(2, 3) numeric(1, !!! x, 4)
#> [1] 1 2 3 4
# As well as unquoting of names: nm <- "yup!" numeric(!!nm := 1)
#> yup! #> 1
# One useful application of splicing is to work around exact and # partial matching of arguments. Let's create a function taking # named arguments and dots: fn <- function(data, ...) { list2(...) } # You normally cannot pass an argument named `data` through the dots # as it will match `fn`'s `data` argument. The splicing syntax # provides a workaround: fn("wrong!", data = letters) # exact matching of `data`
#> [[1]] #> [1] "wrong!" #>
fn("wrong!", dat = letters) # partial matching of `data`
#> [[1]] #> [1] "wrong!" #>
fn(some_data, !!!list(data = letters)) # no matching
#> $data #> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" #> [20] "t" "u" "v" "w" "x" "y" "z" #>
# Empty arguments trigger an error by default: try(fn(, ))
#> list()
# You can choose to preserve empty arguments instead: list3 <- function(...) dots_list(..., .preserve_empty = TRUE) # Note how the last empty argument is still ignored because # `.ignore_empty` defaults to "trailing": list3(, )
#> [[1]] #> #>
# The list with preserved empty arguments is equivalent to: list(missing_arg())
#> [[1]] #> #>
# Arguments with duplicated names are kept by default: list2(a = 1, a = 2, b = 3, b = 4, 5, 6)
#> $a #> [1] 1 #> #> $a #> [1] 2 #> #> $b #> [1] 3 #> #> $b #> [1] 4 #> #> [[5]] #> [1] 5 #> #> [[6]] #> [1] 6 #>
# Use the `.homonyms` argument to keep only the first of these: dots_list(a = 1, a = 2, b = 3, b = 4, 5, 6, .homonyms = "first")
#> $a #> [1] 1 #> #> $b #> [1] 3 #> #> [[3]] #> [1] 5 #> #> [[4]] #> [1] 6 #>
# Or the last: dots_list(a = 1, a = 2, b = 3, b = 4, 5, 6, .homonyms = "last")
#> $a #> [1] 2 #> #> $b #> [1] 4 #> #> [[3]] #> [1] 5 #> #> [[4]] #> [1] 6 #>
# Or raise an informative error: try(dots_list(a = 1, a = 2, b = 3, b = 4, 5, 6, .homonyms = "error")) # dots_list() can be configured to warn when a `<-` call is # detected: my_list <- function(...) dots_list(..., .check_assign = TRUE) my_list(a <- 1)
#> Warning: Using `<-` as argument is often a mistake. #> Do you need to use `=` to match an argument? #> #> If you really want to use `<-`, please wrap in braces: #> #> # Bad: #> fn(a <- 1) #> #> # Good: #> fn(a = 1) # Match 1 to parameter `a` #> fn({ a <- 1 }) # Assign 1 to variable `a`
#> [[1]] #> [1] 1 #>
# There is no warning if the assignment is wrapped in braces. # This requires users to be explicit about their intent: my_list({ a <- 1 })
#> [[1]] #> [1] 1 #>