Title: | Put ... in a Function's Argument List |
---|---|
Description: | Adds ... to a function's argument list so that it can tolerate non-matching arguments. |
Authors: | Nikolas Ivan Krieger [aut, cre] |
Maintainer: | Nikolas Ivan Krieger <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.1.1.9000 |
Built: | 2025-01-29 05:23:41 UTC |
Source: | https://github.com/nikkrieger/withdots |
...
if it does not have itAdds ...
to a closure's args
if it does not have it already.
withdots(f)
withdots(f)
f |
A function. See Handling of primitives in case |
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.
If f
has ...
in its args
, then f
.
Otherwise, a closure: a tweaked version of f
, whose only differences
are:
...
is added to closuresThese are the steps that
withdots()
takes only if f
is a closure without ...
in
its formals
:
environment
(f)
and attributes
(f)
are temporarily saved and
set aside.
If there is a srcref
attribute
among the set-aside
attributes
(f)
, it is removed (see Why the srcref
attribute
is removed below).
The set-aside environment
and attributes
are added back to f
with environment<-
and attributes<-
, respectively.
f
is returned.
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.
srcref
attribute
is removedMany
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: 0x54f7ab0> #> 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: 0x54f7ab0> # 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: 0x54f7ab0> #> 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: 0x54f7ab0> #> attr(,"important_attribute") #> [1] "crucial information" #> attr(,"class") #> [1] "very_special_function" # Success (although the comments in the body() of the function are lost)
# 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")
# 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")