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:

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))
)

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 <- 200L
Error: 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 vectors
  • Double: Double-precision numeric vectors
  • Numeric: Any numeric vectors
  • String: Character vectors
  • Bool: Logical vectors
  • List: List objects
  • Any: Accepts any value
  • Null: Only NULL values
  • Function: Function objects
  • DataFrame: 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 error
Error: 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 happens

It can also be temporarily scoped:

withr::with_options(
  list(sicher.mode = "off"),
  {
    my_var_y %:% Numeric %<-% 10
    my_var_y <- "string"
  }
)