The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
Composable Runtime Contracts for R
The restrictR package lets you define reusable
input contracts from small building blocks using the base pipe
|>. Define a validator once, enforce it anywhere.
Validators compose naturally, support dependent rules via formulas, and
produce clear, path-aware error messages. No DSL, no operator
overloading, just idiomatic R.
library(restrictR)
# Define once
require_positive_scalar <- restrict("x") |>
require_numeric(no_na = TRUE) |>
require_length(1L) |>
require_between(lower = 0, exclusive_lower = TRUE)
# Enforce anywhere
require_positive_scalar(3.14) # passes silently
require_positive_scalar(-1) # Error: x: must be in (0, Inf]
# Found: -1
# At: 1R has no built-in way to define reusable input contracts. Developers
copy-paste the same stopifnot() /
if (!is.numeric(...)) stop(...) blocks across functions.
When the contract changes, you hunt for every validation site. Error
messages are inconsistent: one function says
"x must be numeric", another says
"expected numeric input".
restrictR replaces that with composable, pipe-friendly
validators that:
print() and
as_contract_text().require_newdata <- restrict("newdata") |>
require_df() |>
require_has_cols(c("x1", "x2")) |>
require_col_numeric("x1", no_na = TRUE, finite = TRUE) |>
require_col_numeric("x2", no_na = TRUE, finite = TRUE) |>
require_nrow_min(1L)require_pred <- restrict("pred") |>
require_numeric(no_na = TRUE) |>
require_length_matches(~ nrow(newdata))
# Context is explicit, never magic
require_pred(predictions, newdata = df)newdata$x2: must be numeric, got character
pred: length must match nrow(newdata) (100)
Found: length 50
x: must not contain NA
At: 2, 5, 9
print(require_newdata)
#> <restriction newdata>
#> 1. must be a data.frame
#> 2. must have columns: "x1", "x2"
#> 3. $x1 must be numeric (no NA, finite)
#> 4. $x2 must be numeric (no NA, finite)
#> 5. must have at least 1 row
as_contract_text(require_newdata)
#> "Must be a data.frame. Must have columns: \"x1\", \"x2\". ..."# Install development version from GitHub
# install.packages("pak")
pak::pak("gcol33/restrictR")predict2 <- function(object, newdata, ...) {
require_newdata(newdata)
out <- predict(object, newdata = newdata)
require_pred(out, newdata = newdata)
out
}require_method <- restrict("method") |>
require_character(no_na = TRUE) |>
require_length(1L) |>
require_one_of(c("euclidean", "manhattan", "cosine"))
compute_distance <- function(x, y, method = "euclidean") {
require_method(method)
# ...
}require_survey <- restrict("survey") |>
require_df() |>
require_has_cols(c("age", "income", "status")) |>
require_col_numeric("age", no_na = TRUE) |>
require_col_between("age", lower = 0, upper = 150) |>
require_col_numeric("income", no_na = TRUE, finite = TRUE) |>
require_col_one_of("status", c("active", "inactive", "pending"))#' @param newdata `r as_contract_text(require_newdata)`For domain-specific invariants:
require_weights <- restrict("weights") |>
require_numeric(no_na = TRUE) |>
require_between(lower = 0, upper = 1) |>
require_custom(
label = "must sum to 1",
fn = function(value, name, ctx) {
if (abs(sum(value) - 1) > 1e-8) {
stop(sprintf("%s: must sum to 1, sums to %g", name, sum(value)),
call. = FALSE)
}
}
)| Category | Steps |
|---|---|
| Type checks | require_df(), require_numeric(),
require_integer(), require_character(),
require_logical() |
| Missingness | require_no_na(), require_finite() |
| Structure | require_length(), require_length_min(),
require_length_max(),
require_length_matches(), require_nrow_min(),
require_nrow_matches(),
require_has_cols() |
| Values | require_between(), require_one_of() |
| Columns | require_col_numeric(),
require_col_character(),
require_col_between(),
require_col_one_of() |
| Extension | require_custom() |
“Software is like sex: it’s better when it’s free.” – Linus Torvalds
I’m a PhD student who builds R packages in my free time because I believe good tools should be free and open. I started these projects for my own work and figured others might find them useful too.
If this package saved you some time, buying me a coffee is a nice way to say thanks. It helps with my coffee addiction.
MIT (see the LICENSE file)
@software{restrictR,
author = {Colling, Gilles},
title = {restrictR: Composable Runtime Contracts for R},
year = {2026},
url = {https://github.com/gcol33/restrictR}
}These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.