-
Notifications
You must be signed in to change notification settings - Fork 54
/
Copy pathdots-after-required.qmd
75 lines (50 loc) · 2.78 KB
/
dots-after-required.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# Put `…` after required arguments {#sec-dots-after-required}
```{r}
#| include = FALSE
source("common.R")
```
## What's the pattern?
If you use `…` in a function, put it after the required arguments and before the optional arguments.
This has two positive impacts:
- It forces the user of your function to fully name optional arguments, because arguments that come after `...` are never matched by position or by partial name.
We believe that using full names for optional arguments is good practice because it makes code easier to read.
- This in turn means that uou can easily add new optional arguments or change the order of existing arguments without affecting existing code.
## What are some examples?
The arguments to `mean()` are `x`, `trim`, `na.rm` and `…`.
This means that you can write code like this:
```{r}
x <- c(1, 2, 10, NA)
mean(x, , TRUE)
mean(x, n = TRUE, t = 0.1)
```
Not only does this allow for confusing code[^dots-after-required-1], it also makes it hard to later change the order of these arguments, or introduce new arguments that might be more important.
[^dots-after-required-1]: As much as we recommended people don't write code like this, you know someone will!
If `mean()` instead placed `…` before `trim` and `na.rm`, like `mean2()`[^dots-after-required-2] below, then you must fully name each argument:
[^dots-after-required-2]: Note that I moved `na.rm = TRUE` in front of `trim` because I believe `na.rm` is the more important argument because it's used vastly more often than `trim` and I'm following @sec-important-args-first.
```{r}
mean2 <- function(x, ..., na.rm = FALSE, trim = 0) {
mean(x, ..., na.rm = na.rm, trim = trim)
}
mean2(x, na.rm = TRUE)
mean2(x, na.rm = TRUE, trim = 0.1)
```
## How do I remediate past mistakes?
It's straightforward to fix a function where you've put `...` in the wrong place: you just need to change the argument order and use `rlang::check_dots_used()` to check that no arguments are lost (learn more in @sec-dots-inspect).
This is a breaking change, but it tends to affect relatively little code because most people do fully name optional arguments.
We can use this approach to make a safer version of `mean()`:
```{r}
#| error = TRUE
mean3 <- function(x, ..., na.rm = FALSE, trim = 0) {
rlang::check_dots_used()
mean(x, ..., na.rm = na.rm, trim = trim)
}
mean3(x, , TRUE)
mean3(x, n = TRUE, t = 0.1)
```
::: {.callout-note collapse="true"}
## Base R
In base R you can use `base::chkDots()`, but it uses a slightly simpler technique which means it's not suitable for usage in S3 methods.
:::
## See also
- @sec-dots-data: if `…` is a required argument because it's used to combine an arbitrary number of objects in a data structure.
- @sec-dots-inspect: to ensure that arguments to `…` never go silently missing.