Under what circumstances does !!! work and not work?

(As far as supplying arguments via a list)

I would have thought that the second example would have worked, but I have to really force it to accept the arguments with exec().

library(tidyverse)

df <- tibble(a = c(1,2,3,NA,5), b = a)

# works
map(df, sum, na.rm = TRUE)
#> $a
#> [1] 11
#> 
#> $b
#> [1] 11

# doesn't work
map(df, sum, !!!list(na.rm = TRUE))
#> Error in !list(na.rm = TRUE): invalid argument type

# works with some effort
map(df, ~exec(sum, ., !!!list(na.rm = TRUE)))
#> $a
#> [1] 11
#> 
#> $b
#> [1] 11

Created on 2021-06-17 by the reprex package (v1.0.0)

Hi,

I'm not familiar with exec(), but looking at the documentation, I think you need to switch the order around a bit.

library(tidyverse)

df <- tibble(a = c(1,2,3,NA,5), b = a)

exec(map, !!!list(df, sum, na.rm = TRUE))
#> $a
#> [1] 11
#> 
#> $b
#> [1] 11

Created on 2021-06-17 by the reprex package (v2.0.0)

Hope this helps,
PJ

That definitely works, thanks.

I'm still confused though why the second example didn't work.

The documentation doesn't seem to give any qualifications about what functions it works and doesn't work with.

The big-bang operator !!! forces-splice a list of objects. The elements of the list are spliced in place, meaning that they each become one single argument.

vars <- syms(c("height", "mass"))

# Force-splicing is equivalent to supplying the elements separately
starwars %>% select(!!!vars)
starwars %>% select(height, mass)

Here is an even simpler example:

library(tidyverse)

# works
exec(mean, c(1, 2, NA, 3), !!!list(na.rm = TRUE))
#> [1] 2

# doesn't work
mean(c(1, 2, NA, 3), !!!list(na.rm = TRUE))
#> Error in !list(na.rm = TRUE): invalid argument type

# returns object I don't understand
quo(mean(c(1, 2, NA, 3), !!!list(na.rm = TRUE)))
#> <quosure>
#> expr: ^mean(c(1, 2, NA, 3), na.rm = TRUE)
#> env:  global
expr(mean(c(1, 2, NA, 3), !!!list(na.rm = TRUE)))
#> mean(c(1, 2, NA, 3), na.rm = TRUE)

Created on 2021-06-17 by the reprex package (v1.0.0)

I use !! and !!! in situations where I'm turning strings into symbols. A list of a parameter to pass to a function... I wouldn't have thought about the use of the bang operators in that context.

Hi,

Interesting topic, however I am curios why do you need a list as additional argument to map. I am trying to figure out if quasi-quotations are needed or not .

I want to be able to supply optional additional arguments to child functions with the arguments to the parent function. If I have only one child function with additional arguments, I can use ...

parent <- function(x, y, ...) {

  child(x, y, ...)

}

But with multiple child functions, I need a different solution

parent <- function(x, y, child1_args = list(), child2_args = list()) {

  child1(x, !!!child1_args)

  child2(y, !!!child2_args)
}

I was just paying around and found this which doesn't use rlang.

library(tibble)
library(purrr)

df <- tibble(a = c(1,2,3,NA,5), b = 2*a)

list1 <-  quote(list(.x,na.rm = TRUE))
list2 <- quote(list(.x,na.rm=FALSE))

map(df, ~do.call(sum,eval(list1)))
map(df, ~do.call(sum,eval(list2)))

If you look at the error message, it seems as if the !! is executed instead of !!! and mean is looking for !list(na.rm = TRUE)

# doesn't work
mean(c(1, 2, NA, 3), !!!list(na.rm = TRUE))
#> Error in !list(na.rm = TRUE): invalid argument type

The documentation for !!! has a lot of examples for !!! operations inside quo and expr functions. It's not clear why. But if I try it out, it seems from the expr perspective that !!! is executed correctly.

expr(mean(c(1, 2, NA, 3), !!!list(na.rm = TRUE)))
#> mean(c(1, 2, NA, 3), na.rm = TRUE)

The exec solution is workable, but kind of messy in a pipe because the first argument of exec is the function so you need to supply . as the second argument.

library(tidyverse)

df <- tibble(a = c(1,2,3,NA,5), b = a)

df %>% exec(map, ., sum, !!!list(na.rm = TRUE))
#> $a
#> [1] 11
#> 
#> $b
#> [1] 11

Created on 2021-06-18 by the reprex package (v1.0.0)

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.