Skip to main content

Gradual Typing

Infr is a gradually typed superset of R. This means you can add types incrementally — no annotations means no errors, and each annotation you add gives you more safety.

How it works

In a gradually typed system, unannotated code is treated as having type any — it's not checked but it's not an error either. As you add annotations, Infr checks more of your code:

# No annotations — Infr treats everything as `any`, no errors
x <- 5
y <- "hello"
z <- x + y # No error from Infr (R will error at runtime)
# With annotations — Infr catches the bug
const x: numeric <- 5
const y: character <- "hello"
const z <- x + y
# Error [infr]: Cannot apply `+` to numeric and character

Type inference

You don't need to annotate everything. Infr infers types from:

  • Literals: 5 is numeric, "hello" is character, TRUE is logical
  • Operators: x + 1 is numeric if x is numeric
  • Known functions: paste() always returns character, nrow() returns integer
  • Flow: types propagate through variable assignments
const x <- 5            # inferred: numeric
const y <- "hello" # inferred: character
const z <- x + 10 # inferred: numeric
const w <- x > 3 # inferred: logical
const msg <- paste(y, "world") # inferred: character

The any type

When Infr can't determine a type, it falls back to any. You can also use any explicitly as an escape hatch:

const result: any <- eval(parse(text = some_string))
# No type checking on `result`

Why gradual typing?

R has a massive ecosystem of existing code. Requiring full type annotations upfront would make Infr unusable for real projects. Gradual typing lets you:

  1. Start with zero changes to existing code
  2. Add types where they provide the most value (function signatures, data frames)
  3. Increase strictness over time as your codebase matures

This approach is proven — TypeScript followed the same path for JavaScript, and mypy/Pyright did the same for Python.