Assertive programming follows the principles of fail fast and fail visibly. It is implemented by issuing an informative error message if the function arguments fail to satisfy specific criteria. This is particularly important in R because it is a dynamically typed language.
There are a host of R packages to facilitate assertive programming. Here I am interested in those packages that are
The most useful packages that I am aware of that satisfy these criteria are
This vignette consists of an overview of each package followed by a comparison.
In short the checkr package is recommended over the
alternatives if you are looking for a set of expressive, light-weight,
pipe-friendly assertive functions with customizable object names.
The base package offers the stopifnot()
function.
height <- dplyr::starwars$height
stopifnot(is.character(height))
#> Error:
#> ! is.character(height) is not TRUEThe error message is not that helpful if the user doesn’t know what
is.character() tests for.
In a function it behaves as follows:
The assertthat
package offers assert_that().
library(assertthat)
assert_that(is.character(height))
#> Error:
#> ! height is not a character vectorThe error message is a big improvement on
stopifnot()’s.
In a function it behaves as follows:
my_fun <- function(x) assert_that(is.character(x))
my_fun(dplyr::starwars$height)
#> Error:
#> ! x is not a character vectorPerhaps the neatest feature is the ability to set your own error
message for an assertion function using on_failure().
The checkmate
package provides a plethora of functions to check the type and related
properties of common objects.
library(checkmate)
assert_character(height)
#> Error:
#> ! Assertion on 'height' failed: Must be of type 'character', not 'integer'.In a function it behaves as follows
my_fun <- function(x) assert_character(x)
my_fun(height)
#> Error in `my_fun()`:
#> ! Assertion on 'x' failed: Must be of type 'character', not 'integer'.In addition, the assert() function allows multiple
alternative check** functions (those which return a string
of the error message on failure and a TRUE on success) to
be combined into one assertion.
The checkr
package provides a set of expressive functions to test the values of
objects.
library(checkr)
check_vector(height, "")
#> Warning: `check_vector()` was deprecated in checkr 0.5.1.
#> ℹ Please use chk::chk_vector(), chk::check_dim(), chk::chk_unique(),
#> chk::chk_sorted(), chk::chk_named(), and/or chk::check_values() instead.
#> This warning is displayed once per session.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
#> Error:
#> ! height must be class characterIn a function it behaves as follows:
And multiple alternative checks can be combined using the
checkor() function.
checkor(check_vector(height, ""), check_vector(height, 1))
#> Warning: `checkor()` was deprecated in checkr 0.5.1.
#> ℹ Please use `chk::chkor()` instead.
#> This warning is displayed once per session.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
#> Error:
#> ! height must be class character OR height must be class numericIf all checks are passed stopifnot() returns an
invisible NULL, while assert_that() returns
TRUE. In contrast, checkmate::assert_** and
checkr::check_** return a copy of the original object which
allows them to be used in pipes.
Although there is no way to customize the error message produced by
stopifnot(), assert_that() allows the
programmer to specify the message for a individual test using the
msg argument. In the case of checkmate and
checkr the programmer can specify the variable name using
the .var.name and x_name argument,
respectively. This can be very helpful in functions.
Much of the checkmate package is written in
C to minimize execution time.
Whereas the checkmate package depends on
backports and utils, and
assertthat depends on tools, and
checkr depends on err only base
is dependency-free.