Style guide

knitr::opts_chunk$set(echo = TRUE, eval = FALSE)

Goals

The overall goal of the style guide:

Ultimately, the style guide should help us

Package names

General principles for deciding the name of a package:

Note that the package name and the product name can be stylized differently. Example: tensorflow vs. TensorFlow.

Functional API

All user-facing interactions should have a functional API.

Functions

Function names, argument names, and variable names should use snake_case.

# Good
gs_design_ahr <- function(enroll_rates = ...) {}

# Bad
gsDesignAHR <- function(enrollRates = ...) {}

Methods

This rule also applies to the S3 or R7 method names and argument names, as they are inherently, functions. One should still follow the S3 convention to use dot (.) to connect the method name and class name.

# Good
as_gt.gsDesign <- function(...) {}

# Bad
as.gt.gsDesign <- function(...) {}

Object-oriented API

Object-oriented API should be selectively used for low-level abstractions and limited cases in user-facing interactions when appropriate.

Classes

Class names defined under an OOP system, for example, S3 or R7, should use UpperCamelCase.

This rule intends to indicate the "function" is a class constructor, instead of a (regular) exported or internal function and follows the conventions in R6.

The input argument names and variable names should still use snake_case.

# Good
RangedDoubleOrNULL <- new_class(
  "RangedDoubleOrNULL",
  properties = list(
    value = class_any,
    min = class_double,
    max = class_double,
    min_closed = class_logical,
    max_closed = class_logical
  ),
  ...
)

# Bad
ranged_double_or_null <- new_class(
  "ranged_double_or_null",
  properties = list(
    value = class_any,
    min = class_double,
    max = class_double,
    min_closed = class_logical,
    max_closed = class_logical
  ),
  ...
)

Arguments

Argument order

Arguments should be arranged in the order of data, descriptors, details.

This style makes it natural to consistently create chainable operations that form a workflow under either a functional API or object-oriented API, with minimal surprises.

# Functional API
design <- create_design(enroll_rates, fail_rates, ...)
design |>
  do_compute_1(detail_1 = ...) |>
  do_compute_2(detail_2 = ...)
# Object-oriented API
design <- Design(enroll_rates, fail_rates, ...)
design$do_compute_1(detail_1 = ...)$do_compute_2(detail_2 = ...)

This suggests that if the "data" argument requires multiple inputs, create another function to bundle them as a single object and pass it to the function of interest.

Required and optional arguments

Required arguments should not have a default value. In contrast, optional arguments should have a default value.

Enumerated type arguments

Use lower case abbreviations or snake_case for the enumerated type arguments.

# Bad
f <- function(method = c("AHR", "WLR", ...)) {}

# Good
f <- function(method = c("ahr", "wlr", ...)) {}
# Bad
f <- function(approx = c("event driven", "asymptotic", "generalized schoenfeld", ...)) {}

# Good
f <- function(approx = c("event_driven", "asymptotic", "generalized_schoenfeld", ...)) {}

The reasoning behind this: the strings are used together with function names and argument names, using snake_case would create a visual consistency.

Whenever possible, set the default value of an enumerated type input in the argument definition. See enumerate possible options.

Code formatting

R code should be formatted using styler with the default tidyverse style.

C/C++ code should be formatted using clang-format. This is doable via the Visual Studio Code C++ extension.

The code formatting can be automatically checked in CI/CD workflows.

Code linting

The rules should be enforced via a CI/CD workflow for code linting.

Useful links



Try the gsDesign2 package in your browser

Any scripts or data that you put into this service are public.

gsDesign2 documentation built on April 3, 2025, 9:39 p.m.