Skip to contents

restrictr provides tools for the validation and safe type coercion/recycling of function arguments.

Overview

restrictr and restrict were inspired by MATLAB’s arguments block.

Installation

You can install the development version of restrictr from GitHub with:

# install.packages("pak")
pak::pak("LJ-Jenkins/restrictr")

Examples

abort_if_not can be used for all validations:

library(restrictr)

f <- \(x, y) {
  abort_if_not(
    is.character(x),
    "`{x}` is too short!" = nchar(x) > 5,
    y$x == 1
  )
}

f(1L, list(x = 1))
# Error in `f()`:
# ! Error in `abort_if_not()`
# ℹ Argument `is.character(x)` returned `FALSE`

f("hi", list(x = 1))
# Error in `f()`:
# ! Error in `abort_if_not()`
# ℹ `hi` is too short!

cast_if_not and recycle_if_not provide safe casting and recycling from vctrs. Variables are provided on the left hand side and the expected type/size is provided on the left. Assignment is automatically done back into the environment specified (default is the caller_env()):

f <- \(x, y) {
  cast_if_not(x = double())
  recycle_if_not(y = x)

  print(class(x))
  print(length(y))
}

f(5L, 1)
# [1] "numeric"
# [1] 5

f <- \(x) {
  cast_if_not(x = integer(), .lossy = TRUE)

  print(x)
}

f(1.5)
# [1] 1

f("hi")
# Error in `f()`:
# ! Error in `cast_if_not()`
# ℹ Can't convert `x` <character> to <integer>.

schema, schema_cast and schema_recycle provide the same functionality for data-masked arguments from data.frames/lists. The size of the data.frame/list and whether certain names are present can also be checked using the .names and .size arguments. The data-mask object is returned (unaltered for schema):

f <- \(df) {
  df <- df |>
    schema(x == 1)
}

f(data.frame(x = 2))
# Error in `f()`:
# ! Error in `schema()`
# ℹ Argument `x == 1` for data mask `df` returned `FALSE`.

f <- \(df) {
  df <- df |>
    schema_cast(x = double())

  print(class(df$x))
}

f(data.frame(x = 1L))
# [1] "numeric"

# schema_recycle is only implemented for lists.
f <- \(li) {
  li <- li |>
    schema_recycle(x = 3, y = 5, z = vctrs::vec_size(x))

  print(lengths(li))
}

f(list(x = 1, y = 1, z = 1))
# x y z
# 3 5 3

restrict combines all functionality into a multi-purpose tool. Both variables and named elements within data.frames/lists can be validated and casted/recycled. They keyword functions of validate, cast, lossy_cast, recycle and coerce determines whether any type casting and/or recycling occurs. Validation functions can then be given, either in the form of functions or lambdas. Assignment occurs back into the environment specified (default is the caller_env()). A contradictory example just to show features:

f <- \(df, x) {
  restrict(
    df = validate(
      type = data.frame(x = integer()),
      size = 1,
      ~ ncol(.x) == 1,
      \(.x) colnames(.x) == "x"
    ),
    x = cast(type = .env$x, mask = df),
    x = coerce(type = integer(), size = df$x, lossy = TRUE)
  )

  cat("`df$x` casted to", class(df$x), "from the initial `x` class \n")
  cat("`x` lossily casted to", class(x), "and recycled using value of `df$x` to length", length(x))
}

f(data.frame(x = 3L), 1.5)
# `df$x` casted to numeric from the initial `x` class
# `x` lossily casted to integer and recycled using value of `df$x` to 3

#-- vctrs type/size rules are for all `cast`, `recycle` and `coerce` calls within restrictr functions
f <- \(df) {
  restrict(df = validate(type = data.frame()))
}

f(data.frame(x = 1L, y = "hi"))
# Error in `f()`:
# ! Error in `restrict()`
# ℹ Object `df` is of type <data.frame</ x: integer/ y: character/>>, not <data.frame<>>.

f <- \(x) {
  restrict(x = recycle(size = 10))
}

f(1:5)
# Error in `f()`:
# ! Error in `restrict()`
# ℹ Can't recycle `x` (size 5) to size 10.

Notes

This package is still in the development stage and is subject to change.