add <- typed_function(
function(x, y) x + y,
params = list(x = Numeric, y = Numeric),
.return = Numeric
)
greet <- typed_function(
function(name, title = NULL) {
if (is.null(title)) paste("Hello,", name)
else paste("Hello,", title, name)
},
params = list(name = String, title = Optional(String))
)API Reference
Core Functions
typed_function(fn, params, .return)
Wraps a function with runtime type checks for its parameters and, optionally, its return value.
Parameters: - fn: The function to wrap. Its formals are preserved so callers use the same signature. - params: A named list mapping parameter names to their types (e.g. list(x = Numeric, y = String)). Only listed parameters are checked. Defaults to an empty list (no parameter checking). - .return: Optional return type. When NULL (the default), the return value is not checked. Accepts any sicher_type or sicher_union.
Returns: A function with S3 class "sicher_typed_function" that validates types on every call and delegates argument passing to fn unchanged.
Example:
create_type(name, checker)
Creates a new type with a name and validation function.
Parameters: - name: Character string for error messages - checker: Function that returns TRUE/FALSE for validation
Example:
Positive <- create_type("positive", function(x) is.numeric(x) && all(x > 0))create_list_type(type_spec)
Creates a type for lists with specific field requirements.
Parameters: - type_spec: Named list where names are field names and values are sicher_type or sicher_union objects
Returns: A sicher_type that validates list fields, rejects missing required fields, and rejects unknown fields. The original field specification is stored internally so it can be reused by extend().
Example:
Person <- create_list_type(list(name = String, age = Numeric))extend(base, extra)
Creates a new structured list type by extending an existing one created with create_list_type().
Parameters: - base: A sicher_type returned by create_list_type(). Primitive types and arbitrary create_type() validators cannot be extended because they do not carry a field specification. - extra: Named list of additional fields to merge into the base schema. Values must be sicher_type or sicher_union objects.
Returns: A new sicher_type created from the merged field specification. Fields declared in extra override fields with the same name in base; when that happens, extend() emits a warning.
Example:
Person <- create_list_type(list(
name = String,
age = Numeric
))
Employee <- extend(Person, list(
role = String,
department = Optional(String)
))
Manager <- extend(Employee, list(
reports = Numeric
))create_dataframe_type(col_spec)
Creates a type for data frames with column specifications.
Parameters: - col_spec: Named list where names are column names and values are types
Example:
Table <- create_dataframe_type(list(id = Integer, name = String))Enum(...)
Creates a type that only accepts values from a fixed set.
Parameters: - ...: Allowed scalar values, or a single atomic vector containing the allowed values
Example:
status %:% Enum("draft", "published", "archived") %<-% "draft"
priority %:% Enum(1, 2, 3) %<-% c(1, 2)
status <- "published"
status <- "Ho!"Error: Type error in 'status': Expected enum["draft", "published", "archived"], got string
Received: Ho!
priority <- c(3, 2, 1)
priority <- c(3, 2, 1, 100000)Error: Type error in 'priority': Expected enum[1, 2, 3], got double of length 4
Received: [3, 2, 1, 1e+05]
Enum(...) builds a regular sicher_type, so it works anywhere another type works:
status %:% Enum("draft", "published", "archived") %<-% "draft"
review <- typed_function(
function(state) paste("State:", state),
params = list(state = Enum("draft", "published", "archived")),
.return = String
)
review("published")[1] "State: published"
review("hola")Error: Type error in 'state': Expected enum["draft", "published", "archived"], got string
Received: hola
Literal(...)
Creates a type that only accepts exact scalar atomic literals, inspired by TypeScript literal types.
Parameters: - ...: Allowed scalar atomic literal values
Example:
direction %:% Literal("left", "right") %<-% "left"
direction <- "right"
direction <- c("left", "right")Error: Type error in 'direction': Expected literal["left", "right"], got string of length 2
Received: [left, right]
status_code %:% Literal(200, 404) %<-% 200
status_code <- 404
status_code <- 200LError: Type error in 'status_code': Expected literal[200, 404], got integer
Received: 200
Literal(...) is stricter than Enum(...): it accepts only length-1 atomic values and compares them exactly, so 200 and 200L are different literals.
move <- typed_function(
function(direction) paste("Moving", direction),
params = list(direction = Literal("left", "right")),
.return = String
)
move("left")[1] "Moving left"
move("up")Error: Type error in 'direction': Expected literal["left", "right"], got string
Received: up
ListOf(element_type)
Creates a type for homogeneous lists.
Parameters: - element_type: Type that each list element must satisfy
Example:
Numbers <- ListOf(Numeric)Type Modifiers
Scalar(type)
Requires exactly one element (length 1).
Example:
SingleValue <- Scalar(Numeric)Readonly(type)
Prevents reassignment after initial value.
Example:
Constant <- Readonly(String)Optional(type)
Allows NULL in addition to the base type.
Example:
MaybeString <- Optional(String)Operators
%:%
Type annotation operator.
Usage:
variable %:% Type%<-%
Typed assignment operator.
Usage:
variable %:% Type %<-% value|
Union operator for combining types.
Example:
StringOrNumber <- (String | Numeric)[size]
Size constraint operator.
Example:
ThreeNumbers <- Numeric[3]Built-in Types
Primitive Types
Integer: Integer vectorsDouble: Double-precision numeric vectorsNumeric: Any numeric vectorsString: Character vectorsBool: Logical vectorsList: List objectsAny: Accepts any valueNull: Only NULL valuesFunction: Function objectsDataFrame: Data frame objects
Error Handling
Type errors provide detailed context:
x %:% Numeric %<-% "text"Error: Type error in 'x': Expected numeric, got string
Received: text
Error messages include: - Expected type - Actual type - Variable name (when available) - Value preview (for debugging)
Global Options
The behavior of sicher can be controlled globally using R’s options() mechanism. These options allow you to switch between strict type enforcement and a fully disabled mode depending on your use case.
my_var_x %:% Numeric %<-% 10 # validated and bound with type enforcement
my_var_x <- "string" # trigger an errorError: Type error in 'my_var_x': Expected numeric, got string
Received: string
options(sicher.mode = "off")
my_var_y %:% Numeric %<-% 10
my_var_y <- "string" # nothing happensIt can also be temporarily scoped:
withr::with_options(
list(sicher.mode = "off"),
{
my_var_y %:% Numeric %<-% 10
my_var_y <- "string"
}
)