The R Style Guide describes the rdev style conventions, along with automated checks (styler and lintr). The rdev R Style Guide incorporates both the Tidyverse Style Guide and Google’s R Style Guide.
rdev conventions
rdev uses a mix of Google and tidyverse conventions.
Returns
rdev typically uses implicit returns unless needed for flow control or to construct a specific return structure.
Qualifying namespaces
Use of @import
and @importFrom
are
discouraged, instead using ::
in functions defined in
R/
. An exception is infix functions (%name%
),
which are always imported, using roxygen @importFrom
.
Within R and Quarto Notebooks, use of library()
is
encouraged for readability.
Package-level documentation
rdev package documentation lives in R/package.R
.
Additionally, use of base R is encouraged within functions defined in
R/
(to control dependencies), where tidyverse R is
encouraged within notebooks for readability.
R Markdown documents should generally wrap at the same width as code, with exceptions for the R Notebook ‘description line’ (the first non-blank line in the body), and long hyperlinks.
styler
The rdev package implements styler using the styler defaults,
with permanent caching enabled
(options(styler.cache_root = "styler-perm")
). The
style_all()
function applies the tidyverse style to all R
and Quarto files (R
, Rprofile
,
Rmd
, Rnw
, qmd
) in the package
directory structure.
lintr
The lint_all()
function lints all R and Quarto files
(R
, Rmd
, qmd
, Rnw
,
Rhtml
, Rpres
, Rrst
,
Rtex
, Rtxt
) with lintr using an opinionated
configuration (.lintr
):
linters: linters_with_tags(
tags = NULL,
extraction_operator_linter = NULL,
implicit_integer_linter = NULL,
line_length_linter(100),
missing_package_linter = NULL,
namespace_linter = NULL,
nonportable_path_linter = NULL,
paste_linter(allow_file_path = "always"),
todo_comment_linter = NULL,
undesirable_function_linter(
within(all_undesirable_functions, rm(ifelse, library, require, structure))
),
undesirable_operator_linter(all_undesirable_operators),
unnecessary_concatenation_linter(allow_single_expression = FALSE),
unused_import_linter(
except_packages = c("bit64", "data.table", "tidyverse", pkgload::pkg_name("."))
)
)
These settings were deliberately chosen to meet specific goals:
Use all linters
This selects all linters, including the new Google linters. By selecting all linters, not just the defaults, this forces evaluation of new linters as they are added to lintr. Specific linters are disabled or configured per rdev style.
Allow $ extraction operator
The extraction operator linter enforces use of [[
over
$
. Since $
is intended for interactive use and
supports partial mapping, it is safer to use [[
. However,
use of $
is common practice, including within the lintr
code, and leads to more readable code.
Allow implicit integers
The implicit integer linter checks that integers are explicitly typed
using the form 1L
. Again, while this is safer, implicit
integers are common practice, including in the output of
dput()
:
Set line length
A line length of 100 is a reasonable width when working in the RStudio IDE on a laptop.
Use renv for missing packages
The missing package linter and the namespace linter both check for
packages referenced in code that are not installed. rdev uses renv to manage packages,
which does a better job of detecting missing packages, including not
complaining when the current package ("."
) is not
installed.
Allow non-portable paths
The non-portable path linter generates too many false positives,
including calls to fs, which
essentially makes UNIX-style paths portable across operating systems.
Similarly, the allow_file_path
default option of
"double_slash"
generates false positives when used with
fs.
Allow TODO comments
rdev conventions encourage use of # TODO:
comments as a
convenient way of identifying needed or planned updates inline.
Disallow almost all undesirable functions and operators
undesirable_function_linter(
within(all_undesirable_functions, rm(ifelse, library, require, structure))
),
undesirable_operator_linter(all_undesirable_operators),
Enable all undesirable functions and operators, not just the
defaults, except for ifelse()
which is more succinct than
an if
/else
block, library()
,
require()
, and structure()
. Use of
require()
within .Rprofile
and
library()
within R Markdown documents is an appropriate and
common practice, although neither should be used within package code.
structure()
is useful for comparing test results to values
generated with dput()
.