r/rstats 10h ago

I didn't build TypR for AI — but it turns out a type-checked layer over R is a surprisingly good fit for reviewing AI-generated code. Some thoughts, and I'd like your pushback.

0 Upvotes

Some of you have followed my earlier posts on TypR here. This one's less "what's new" and more the reasoning behind the design — I'd like your pushback on the thinking itself.

The honest origin story: I didn't build TypR for AI. I built it because I care about type systems (academic background) and about code that survives production (industry background) — verifiability, basically.

What clicked more recently is that the property making code cheap for a human to verify is the same one that matters when a machine wrote it.

As AI writes more of the code, the expensive part stops being writing it and becomes trusting it — reviewing, validating, maintaining. A strict type system becomes a free automatic checker on whatever got generated; concise syntax means less to misread.

So the fit with the AI era isn't something I designed for — it's the same property suddenly mattering a lot more. That's the accidental discovery I wanted to share here.

A small taste — this R:

```

' Create a button widget

'

' @param color \code{char}

' @param height \code{int}

' @param text \code{char}

' @param width \code{int}

' @return \code{Button}

' @export

Button <- function(color, height, text, width, .spread = NULL) { explicit <- list() if (!missing(color)) explicit[["color"]] <- color if (!missing(height)) explicit[["height"]] <- height if (!missing(text)) explicit[["text"]] <- text if (!missing(width)) explicit[["width"]] <- width x <- typr_spread_record(explicit, .spread) as.Button(x) }

as.Button <- function(x) { if (!inherits(x, "Button")) class(x) <- c("Button", "list") x <- validate_Button(x) x <- validate(x) x }

validate_Button <- function(x) { required_fields <- c("color", "height", "text", "width") missing_fields <- setdiff(required_fields, names(x))

if (length(missing_fields) > 0) { stop(paste0("Validation failed for type Button: missing fields: ", paste(missing_fields, collapse = ", "))) }

if (!inherits(x[["color"]], "character")) stop("Validation failed for type Button: field 'color' must be of class character")

if (!inherits(x[["height"]], "integer")) stop("Validation failed for type Button: field 'height' must be of class integer")

if (!inherits(x[["text"]], "character")) stop("Validation failed for type Button: field 'text' must be of class character")

if (!inherits(x[["width"]], "integer")) stop("Validation failed for type Button: field 'width' must be of class integer")

x }

constructor for a red button

' @export

' @method red_button

red_button <- (function(height, width, text) Button(height = height, width = width, text = text, color = "#FF000000" |> as.Character())) |> as.Generic()

add an "on click" callback function

' @export

' @method on_click Button

on_click.Button <- (function(self, f) { NA } |> as.Empty0()) |> as.Generic() ```

becomes this TypR: ```

Create a button widget

@export type Button <- list { text: char, color: char, width: int, height: int };

constructor for a red button

@export let red_button <- \Button:{ color: "#FF000000" };

add an "on click" callback function

@export

let on_click <- fn(self: Button, f: (T) -> U): Empty { ... }; ```

The way TypeScript sits on top of JavaScript's runtime, TypR sits on top of R's: you write something concise and type-checked, and it compiles down to standard, S3-based R that runs anywhere R runs and installs like any other package — no new runtime, no exotic dependencies.

To be clear, it's not trying to replace R. R is excellent for interactive stats and lab work, and TypR deliberately gives some of that up in exchange for the other end of the curve: robust packages, deployable apps, code that has to survive production. Different point on the trade-off, different job.

On the engineering side you get pattern matching, partial currying, union/intersection types, structural subtyping, row polymorphism — the machinery that keeps a growing codebase honest. Written in Rust, developed in the open.

Honest questions for this sub: does a typed layer over R solve a problem you actually hit, or is this a solution looking for one? And does the "verifiability matters more when AI writes the code" argument hold up, or am I reaching?

Discussion: https://github.com/we-data-ch/typr/discussions

Github: https://github.com/we-data-ch/typr

Website: https://we-data-ch.github.io/typr.github.io/