This post is more about reasoning behind the design of a programming language I made: TypR — 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\](https://github.com/we-data-ch/typr/discussions)
Github: [https://github.com/we-data-ch/typr\](https://github.com/we-data-ch/typr)
Website: [https://we-data-ch.github.io/typr.github.io/\](https://we-data-ch.github.io/typr.github.io/)