Skip to content
LogoLogo

Why VibeLang

Why application structure belongs in the compiler

TypeScript describes values well, but serious applications also need to describe expected errors, dependencies, validation boundaries, and durable execution.

The Missing Parts

async function getUser(id: string): Promise<User> {
  const response = await fetch(`/users/${id}`)
  if (!response.ok) throw new Error("request failed")
  return response.json()
}

The signature hides several facts: the operation can fail, it depends on ambient networking, the response is unvalidated, and tests must intercept global behavior.

The Library Answer

Effect systems implemented as libraries make success, failure, and requirements explicit. That model is valuable, but it introduces a second execution vocabulary and a runtime interpreter.

The Compiler Answer

VibeLang keeps the useful information in ordinary-looking code while representing expected failure honestly as a value:

async function getUser(
  id: string,
): Promise<Result<User, NotFound | Timeout | InvalidResponse>> {
  const http = HttpClient.context()
  const response = (await http.get(`/users/${id}`)).unwrap()
  if (response.status === 404) throw new NotFound(id)
  return (await response.json(User)).unwrap()
}

The compiler lifts plain success returns and Error throws into Result variants. It also tracks HttpClient.context() as a requirement until a Layer provides it. No caller manually threads context.

Compiler support enables:

  • Result/error inference through ordinary calls
  • exhaustive matching over known Error unions
  • one visible Promise<Result<A, E>> model for async failure
  • validators derived from ordinary types at comptime
  • target selection and platform capability checking
  • durable Plan IR lowered statically from checked source

Why Result Values?

An earlier design erased a separate failure row and required new propagation and catch grammar. Using Result<A, E> makes the public contract readable by existing TypeScript tooling and removes most parser changes. Compiler lifting preserves concise bodies, while callers still see and must use the failure value.

Why Durable Execution Belongs Here

Durable runtimes commonly repeat schemas, error shapes, dependencies, and RPC bindings that the compiler already knows. Passing a statically resolvable function to durable(...) lets VibeLang lower checked code into Plan IR and derive those boundaries. Planning reads emitted IR; it never executes the Flow or its Actions.

Durability remains opt-in. Ordinary functions do not pay for a scheduler.

When VibeLang Fits

VibeLang is for TypeScript teams that want:

  • expected failures as must-use values with concise function bodies
  • explicit transitive dependencies without manual context arguments
  • portable services across JavaScript, native, Wasm, and tests
  • type-driven validation and asset loading
  • statically analyzable durable workflows

The project is not production-ready today, and unconstrained JavaScript reflection will remain incompatible with some native or static-analysis guarantees.