s7x provides utility extensions to the {S7} type system.
It fills the void of features I’ve long wanted in S7 (and R) to help make R packages more type safe(ish) with less boilerplate.
Notably s7x provides:
- an extensible
Enumclass for speficying enumerations - scalar properties
- range propeties (discrete and continuous)
- property unions and intersections
See Enums in R: towards type safe R for the motivation behind this package.
Installation
You can install the development version of s7x like so:
pak::pak("josiahparry/s7x")[!NOTE]
s7xis built on the development version ofS7. Install it withpak::pak("RConsortium/S7").
Enum(eration)s
In R, we use enumerations all the time, though in formally. For example, stats::cor()’s argument method = c("pearson", "kendall", "spearman") is an enumeration in that method can be only one of three possible values. In R, enums are typically strings and validated via arg.match() or the equivalent rlang::arg_match().
In s7x, enums are formalized through the s7x::Enum class.
library(s7x)
# create a new enum
CorMethod <- new_enum("CorMethod", c("pearson", "kendall", "spearman"))
# instantiate the enum
x <- CorMethod("pearson")
x
#> <CorMethod>
#> @ value : chr "pearson"
#> @ variants: chr [1:3] "pearson" "kendall" "spearman"Note that all s7x::Enums can be cast as character vectors.
as.character(x)
#> [1] "pearson"Thus, we can use the Enum to enforce variants, but when we need the underlying value we can as.character() or extract it’s value via x@value.
Scalar properties
A longtime wish list item of mine is scalar properties. A scalar in R is a length one vector. These are useful for arguments such as n_components = 5—where it wouldn’t make sense for the value to be c(5, 1), for example.
S7 has built-in classes for the vector types in R (double, integer, character, list) but not for their scalar variants.
s7x introduces the property_scalar() function which lets you create scalar variants.
For example:
library(S7)
NComponents := new_class(properties = list(n = property_scalar(class_integer)))
n_comps <- NComponents(5L)
n_comps
#> <NComponents>
#> @ n: int 5Providing a non-length 1 value creates an error:
NComponents(c(5L, 1L))
#> Error in `NComponents()`:
#> ! <NComponents> object properties are invalid:
#> - @n must be a scalar atomic