-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathchk.Rmd
142 lines (106 loc) · 4.94 KB
/
chk.Rmd
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
---
title: "Get Started with chk"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Get Started with chk}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
## Introduction
R functions and packages are great for sharing code, but it's hard to predict how end-users will implement them.
Users might pass inputs that your function wasn't designed to handle, leading to incorrect results or errors.
For example, if a function expects a numeric vector (e.g., `c(1, 2, 3)`) but receives a character vector (e.g.,` c("1", "2", "3")`), it may return an error.
The `chk` package provides a set of functions that check user-provided arguments and deliver meaningful error messages to guide users when something goes wrong.
Including `chk`'s specially designed functions when developing your R package will help improve user experience, prevent errors, and make your code more robust by ensuring consistent behavior across different inputs.
This enhances the reliability and reusability of your code.
Additionally, it can help with performance optimization by ensuring that your functions receive inputs of the appropriate size, thereby avoiding time-consuming calculations.
## Goal
`chk` is an R package for developers
to check user-supplied function arguments.
It is designed to be simple, customizable and fast.
## `chk` Functions
Based on the function prefixes, we can classify `chk` functions into three categories:
1. `chk_` Functions
2. `vld_` Functions
3. `check_` Functions
### 1. `chk_` Functions
`chk_` functions check the properties of individual objects.
For example,
`chk_flag(x)` checks whether `x` is a flag, i.e., a non-missing logical vector of length 1.
`chk_` functions are called for their side-effects,
i.e., they throw an informative error if the object fails the check.
Although do return an invisible copy of the first argument so they can be used in pipes.
```{r, error=TRUE}
library(chk)
y <- TRUE
chk_flag(y)
y <- NA
chk_flag(y)
```
The error messages, which follow the [tidyverse style guide](https://style.tidyverse.org/errors.html), are designed to allow the user to quickly identify the problem with the argument value(s) they are providing.
The errors are [rlang errors](https://rlang.r-lib.org/reference/abort.html) of subclass `'chk_error'`.
### 2. `vld_` Functions
Each `chk_` function has a matching `vld_` function
which returns a flag indicating whether the object passed the check.
```{r, error=TRUE}
vld_flag(TRUE)
vld_flag(NA)
```
The `vld_` functions allow developers to provide their own error messages.
```{r, error=TRUE}
if (!vld_flag(NA)) abort_chk("`NA` is not TRUE or FALSE!!")
```
### 3. `check_` Functions
The `check_` functions are more complex then the `chk_` functions which make them slower but
makes doing some general tests easier.
## Using chk
The `chk_` functions are designed to be used within functions.
Consequently, when constructing an error message they use the name of the object that they received
as this is expected to be the name of the argument.
```{r, error = TRUE}
fun1 <- function(x) {
chk_whole_number(x)
# use x
}
fun1(1)
y <- 1.3
fun1(x = y)
```
If this is not the case, developers can provide a different name using the `x_name` argument.
```{r, error = TRUE}
x <- NA
chk_flag(x, x_name = "`zzz`")
```
**IMPORTANT NOTE**
As the `chk_` (and `vld_`) functions are not expected to be directly exposed to users they don't check any of their arguments (other than the object of interest of course!) to ensure that they are as fast as possible.
## Extending chk
The `chk_flag()` function illustrates the general structure of a `chk_` function.
``` r
chk_flag
#> function(x, x_name = NULL){
#> if(vld_flag(x)) return(invisible(x))
#> if(is.null(x_name)) x_name <- deparse_backtick_chk(substitute(x))
#> abort_chk(x_name, " must be a flag (TRUE or FALSE)")
#> }
#> <bytecode: 0x7fe802835670>
#> <environment: namespace:chk>
```
A `chk_` function initially checks the object (using its `vld_` partner) and if the object passes the check immediately returns an invisible copy of the object.
If, and only if, the object fails the check does the `chk_` function construct and then throw an informative error message.
The `deparse_backtick_chk()` and `abort_chk()` functions are exported to make it easy for programmers to develop their own `chk_` functions.
The [chk-lgl.R](https://github.com/poissonconsulting/chk/blob/master/R/chk-lgl.R) script illustrates
the general template to use when developing your own `chk_` functions.
### `abort_chk()`
The `abort_chk()` function converts multiple arguments to a string using `paste0(..., collapse = '')` and provides number sensitive `sprintf`-like types.
By default it also capitalizes the first character
and adds a missing period.
```{r, error=TRUE}
abort_chk("There %r %n problem director%y%s.", n = 1)
abort_chk("there %r %n ", "problem director%y%s", n = 2)
```