Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce check_dots_named() #35

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

salim-b
Copy link

@salim-b salim-b commented Jun 10, 2020

This PR adds the function check_dots_named() which ensures that dots are either empty (if .empty_ok = TRUE) or all named dot parameter names are a valid subset of a function's parameter names.

It is intended to combat a major downside of using ... which is phrased as follows in chapter 6.6 of the book Advanced R:

Using ... comes with two downsides: (...) A misspelled argument will not raise an error. This makes it easy for typos to go unnoticed:

sum(1, 2, NA, na_rm = TRUE)
#> [1] NA

We can use check_dots_named() to address this second downside:

sum_safe <- function(...,
                     na.rm = FALSE) {
    check_dots_named(...,
                     .function = sum)
    sum(...,
        na.rm = na.rm)
}

sum_safe(1, 2, NA, na_rm = TRUE)
#> Error: Invalid argument provided in `...`: `na_rm`
#> Only unnamed arguments are valid for `sum()`.

It can also be used to build an sapply() function that fails "intelligently":

sapply_safe <- function(X,
                        FUN,
                        ...,
                        simplify = TRUE,
                        USE.NAMES = TRUE) {
  ellipsis::check_dots_named(...,
                             .function = FUN)
  sapply(X = X,
         FUN = FUN,
         ...,
         simplify = TRUE,
         USE.NAMES = TRUE)
}

sapply_safe(list(1:5, 1),
            paste,
            "hour schedules",
            sep = "-",
            collaspe = ", ")
#> Error: Invalid argument provided in `...`: `collaspe`
#> Valid arguments for `paste()` include: `...`, `sep`, `collapse` and `recycle0`
#> Did you mean `collapse`?

The code to suggest valid argument names is largely borrowed from rlang::arg_match().

Internally I rely on the additional packages purrr, checkmate, utils and methods. Of course, checkmate could be ditched (at the cost of not doing argument checks) and purrr::walk() could be replaced by invisible(lapply()) or a simple for-loop if desired. Just let me know whatever you prefer.

Caveats regarding check_dots_named():

  • unnamed dots arguments are not checked (not the purpose of this function). Use check_dots_used() in addition to check_dots_named() to ensure that all named arguments are valid and all unnamed arguments are consumed.

  • It cannot properly check argument names of functions which only declare them in a "submethod" (or whatever this is called) like base::seq().

    The following throws an error:

    sapply_safe(X = c(0,50),
                FUN = seq,
                to = 100,
                by = 5)
    #> Error: Invalid argument provided in `...`: `to`
    #> Valid arguments for `seq()` include: `...`

    while providing to and by unnamed is fine:

    sapply_safe(X = c(0,50),
                FUN = seq,
                100,
                5)
    #> [[1]]
    #> [1]   0   5  10  15  20  25  30  35  40  45  50  55  60  65  70  75  80  85  90  95 100
    #> 
    #> [[2]]
    #>  [1]  50  55  60  65  70  75  80  85  90  95 100

    If anyone knows how the parameter names of this type of functions could be determined, please let me know!

  • It doesn't allow named objects as input for unnamed ... args, i.e. it might be a bit rash:

    sum_safe(a = 1, b = 2)}
    #> Error: Invalid argument provided in `...`: `a`
    #> Only unnamed arguments are valid for `sum()`.
    
    # while the original function actually has nothing to complain:
    sum(a = 1, b = 2)
    #> [1] 3
  • Although I tried to, I'm not 100% sure if I got the terminology of arguments vs. parameters always right... 🤓🤪

salim-b added 8 commits June 10, 2020 16:46
`check_dots_named()` ensures that dots are either empty or all dot parameter names are a valid subset of a function's parameter names.
There was a (seemingly) important fix regarding `methods::formalArgs()` in R 3.6.0, so this version is used as the minimally required one. Cf. https://cran.r-project.org/doc/manuals/r-devel/NEWS.3.html
- provide name of the function the dots are meant for (passed on to) in the error message

- improve error message when no named dots are allowed.

- explicitly state in error message when a `.forbidden` dot arg name is provided.

- improve/extend documentation and examples
@salim-b
Copy link
Author

salim-b commented Jun 14, 2020

I have now added the logic to determine the name of the function the dots are meant for (passed on to) and overhauled the code:

  • provide name of the function the dots are meant for (passed on to) in the error message
  • improve error message when no named dots are allowed.
  • explicitly state in error message when a .forbidden dot arg name is provided.
  • improve/extend documentation and examples

Feedback welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant