withdots {withdots} | R Documentation |
Give a function ...
if it does not have it
Description
Adds ...
to a closure's args
if it does not have it already.
Usage
withdots(f)
Arguments
f |
A function. See Handling of primitives in case |
Details
If f
already has ...
in its args
, then it is returned with no
changes. Otherwise, ...
is added to f
's formals and then f
is
returned. See Handling of primitives below.
Value
If f
has ...
in its args
, then f
.
Otherwise, a closure: a tweaked version of f
, whose only differences
are:
How ...
is added to closures
These are the steps that
withdots()
takes only if f
is a closure without ...
in
its formals
:
-
attributes
(f)
are temporarily saved and set aside. If there is a
srcref
attribute
among the set-asideattributes
(f)
, it is removed (see Why thesrcref
attribute
is removed below).The remaining set-aside
attributes
are added back tof
withattributes<-
.-
f
is returned.
Handling of primitives
If f
is primitive and already has
...
in its args
(e.g., c()
, rep()
, max()
), then it is
returned as is.
If f
is primitive and does not have ...
in its args
,
then an error will be thrown. The user can bypass this error by processing
f
with rlang::as_closure()
before passing it to withdots()
.
However, keep in mind that the argument matching behavior of the
resulting closure may be different from what is expected, since
primitives may use nonstandard argument matching.
Why the srcref
attribute
is removed
Many
functions—including those created with function()
—have a srcref
attribute
. When a function is printed, print.function()
relies on this attribute
by default to depict the function's
formals and body.
withdots()
adds ...
via formals<-
, which expressly drops
attributes
(see its documentation page). To prevent this
loss, withdots()
sets attributes
(f)
aside at the beginning and
re-attaches them to f
at the end. Normally, this would re-attach the
original f
's srcref
attribute
to the new f
, making it so
that the newly added ...
would not be depicted when the new f
is printed. For this reason, the old srcref
attribute
is
dropped, and only the remaining attributes
are re-attached to the new
f
.
Observe what would happen during printing if all original
attributes
(f)
were naively added to the modified f
:
# Create a function with no dots: foo <- function(a = 1) { # Helpful comment a } # Give it important attributes that we can't afford to lose: attr(foo, "important_attribute") <- "crucial information" class(foo) <- "very_special_function" # Print foo, which also prints its important attributes: foo #> function(a = 1) { #> # Helpful comment #> a #> } #> <environment: 0x571c620> #> attr(,"important_attribute") #> [1] "crucial information" #> attr(,"class") #> [1] "very_special_function" # Save its attributes: old_attributes <- attributes(foo) # Add dots: formals(foo)[["..."]] <- quote(expr = ) # See that the important attributes have been dropped: foo #> function (a = 1, ...) #> { #> a #> } #> <environment: 0x571c620> # Add the attributes back: attributes(foo) <- old_attributes # Print it again, and we see that the attributes have returned. # However, the ... disappears from the argument list. foo #> function(a = 1) { #> # Helpful comment #> a #> } #> <environment: 0x571c620> #> attr(,"important_attribute") #> [1] "crucial information" #> attr(,"class") #> [1] "very_special_function" # We know the actual function definitely has dots, since it can handle # extraneous arguments: foo(1, 2, junk, "arguments", NULL) #> [1] 1 # Remove the "srcref" attribute, and the function is printed accurately. # Furthermore, its important attributes are intact: attr(foo, "srcref") <- NULL foo #> function (a = 1, ...) #> { #> a #> } #> <environment: 0x571c620> #> attr(,"important_attribute") #> [1] "crucial information" #> attr(,"class") #> [1] "very_special_function" # Success (although the comments in the body() of the function are lost)
Examples
# The base::match() function has no ... and can't handle extraneous arguments
if (FALSE) {
match("z", letters, cannot_handle_ = "junk arguments")
}
# But if we give it dots...
match_with_dots <- withdots(match)
# ...it can now handle extraneous arguments:
match_with_dots("z", letters, can_now_handle = "junk arguments")