If we view the body of a function that has dots ...
in its argument list, we can usually find the function that receives those dots arguments.
For example, we can see in the body of sapply()
that the dots arguments are passed through to lapply()
.
sapply
# function (X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
# {
# FUN <- match.fun(FUN)
# answer <- lapply(X = X, FUN = FUN, ...)
# ## rest of function body
# }
# <bytecode: 0x000000000e05f0b0>
# environment: namespace:base>
However, in lapply()
, there are dots ...
in the argument list but not in the function body.
lapply
# function (X, FUN, ...)
# {
# FUN <- match.fun(FUN)
# if (!is.vector(X) || is.object(X))
# X <- as.list(X)
# .Internal(lapply(X, FUN))
# }
# <bytecode: 0x0000000009414f08>
# <environment: namespace:base>
So where do the dots ...
arguments in lapply()
get processed? What/where are they passed to? We cannot pass them to match.fun()
. I presume they are passed into .Internal()
but I don't see any reason for this to work when I don't see them passed into any function in the function body.
They're not explicitly passed to .Internal
, but I believe they're available to do_lapply
(in src/main/apply.c) via dynamic scope. The scoping rules may be slightly different than usual, since .Internal
is a primitive function.
You can see that ...
(R_DotsSymbol
) is added to the function call lapply
creates, so they're available to the function call on each list element. tmp
is roughly equivalent to X[[i]]
and R_fcall
is roughly equivalent to FUN(X[[i]], ...)
.
SEXP tmp = PROTECT(LCONS(R_Bracket2Symbol,
LCONS(X, LCONS(isym, R_NilValue))));
SEXP R_fcall = PROTECT(LCONS(FUN,
LCONS(tmp, LCONS(R_DotsSymbol, R_NilValue))));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With