The ultimate goal in the question is to construct the following unevaluated call using r's computing on the language, where list
, a_name
and 50L
are provided from parameters.
list(a_name = 50L)
Which internally looks like
str(quote(list(a_name = 50L)))
# language list(a_name = 50L)
str(as.list(quote(list(a_name = 50L))))
#List of 2
# $ : symbol list
# $ a_name: int 50
I will put my variables in a list so the further code will be cleaner.
params = list(my_fun = as.name("list"), my_name = "a_name", my_value = 50L)
# What I tried so far?
# 1. The first thing that one would try
substitute(my_fun(my_name = my_value),
params)
#list(my_name = 50L) ## `my_name` was not substituted!
# 2. Workaround get the same output, but only after `setNames` call evaluation, so doesn't really answer the question about constructing specific call
substitute(setNames(my_fun(my_value), my_name), ## alternatively could be `structure(..., names=my_name)`
params)
#setNames(list(50L), "a_name")
# 3. Another workaround, not really computing on the language but parsing, and integer L suffix is gone!
with(expr = parse(text=paste0(my_fun, "(", my_name, " = ", my_value, ")"))[[1L]],
data = params)
#list(a_name = 50)
# 4. Working example using rlang
with(expr = rlang::call2(my_fun, !!my_name := my_value),
data = params)
#list(a_name = 50L)
Is there any way in base r to construct required call? Basically to get exactly same output as rlang
way but using base r.
Note that this Question is not a duplicate of this which was strictly asking for rlang
solution. This Question asks for a way to achieve it using base r. If there is no way to achieve it, I would like to know that as well. Thank you.
Assuming that the question is how to produce a call
object whose function is the first argument of params
, whose single argument name is the value of the second component of params
and whose argument value is the third component of params
then c2
is that call object. We verify that the cakl object produced is identical to the rlang call object shown in the question.
c2 <- as.call(setNames(params, c("", "", params2$my_name))[-2])
# verify that c2 equals the output of the rlang example in question
c1 <- with(expr = rlang::call2(my_fun, !!my_name := my_value), data = params)
identical(c1, c2)
## [1] TRUE
This generalizes so that if the call has more arguments so that params has length n+2 and the second component of params
is a vector of names whose length is n then it still works.
If you have control over the input I think it would make more sense to define the input list as having one component for the function name and one component for each argument with the names of the components being the argument names (as opposed to a separate argument to hold the names). In that case we could simply write the following. Actually what the above does is to transform params into that form and then use as.call so we might as well provide that form from the start if possible.
as.call(params2) # params2[[1]] is func name, params2[-1] is named args
To execute the call just use:
eval(c2)
or we could use do.call
:
do.call(as.character(params[[1]]), setNames(tail(params, -2), params[[2]]))