The Language That
Changes Everything

Algebraic effects. Behavioral types. Schema evolution.
Compile to native C or JavaScript. One language, every platform.

Not Just Another Functional Language

Lux solves problems other languages can't even express

ALGEBRAIC EFFECTS

Side effects in the type signature. Swap handlers for testing. No dependency injection frameworks.

BEHAVIORAL TYPES

Prove functions are pure, total, deterministic, or idempotent. The compiler verifies it.

SCHEMA EVOLUTION

Built-in type versioning with automatic migrations. Change your data types safely.

DUAL COMPILATION

Same code compiles to native C (via GCC) or JavaScript. Server and browser from one source.

NATIVE PERFORMANCE

Beats Rust and Zig on recursive benchmarks. Zero-cost effect abstraction via evidence passing.

BATTERIES INCLUDED

Package manager, LSP, REPL, debugger, formatter, test runner. All built in.

Effects: The Core Innovation

Every side effect is tracked in the type signature

fn processOrder(
  order: Order
): Receipt
  with {Sql, Http, Console} =
{
  let saved = Sql.execute(db,
    "INSERT INTO orders...")
  Http.post(webhook, order)
  Console.print("Order {order.id} saved")
  Receipt(saved.id)
}

The signature tells you everything:

  • Sql — Touches the database
  • Http — Makes network calls
  • Console — Prints output

No surprises. No hidden side effects. Ever.

Testing Without Mocks or DI Frameworks

Swap effect handlers at test time. Same code, different behavior.

// Production: real database, real HTTP
run processOrder(order) with {
  Sql -> postgresHandler,
  Http -> realHttpClient,
  Console -> stdoutHandler
}
// Test: in-memory DB, captured calls
run processOrder(order) with {
  Sql -> inMemoryDb,
  Http -> captureRequests,
  Console -> devNull
}

No Mockito. No dependency injection. Just swap the handlers.

Behavioral Types: Compile-Time Guarantees

Prove properties about your functions. The compiler enforces them.

// The compiler verifies these properties

fn add(a: Int, b: Int): Int
  is pure is commutative = a + b

fn factorial(n: Int): Int
  is total = if n <= 1 then 1 else n * factorial(n - 1)

fn processPayment(p: Payment): Result
  is idempotent = // Safe to retry on failure
  ...

fn hash(data: Bytes): Hash
  is deterministic = // Same input = same output
  ...

is pure

No side effects. Safe to memoize.

is total

Always terminates. No infinite loops.

is idempotent

Run twice = run once. Safe for retries.

Schema Evolution: Safe Data Migrations

Built-in type versioning. No more migration headaches.

type User @v1 { name: String }

type User @v2 {
  name: String,
  email: String,
  from @v1 = {
    name: old.name,
    email: "unknown@example.com"
  }
}

type User @v3 {
  firstName: String,
  lastName: String,
  email: String,
  from @v2 = {
    firstName: String.split(old.name, " ").head,
    lastName: String.split(old.name, " ").tail,
    email: old.email
  }
}

Load old data with new code. The compiler ensures migration paths exist.

One Language, Every Platform

Compile to native C or JavaScript from the same source

# Compile to native binary (via GCC)
lux compile server.lux
./server  # Runs natively

# Compile to JavaScript
lux compile client.lux --target js
node client.js  # Runs in Node/browser

Same code, different targets:

  • Native C — Maximum performance, deployable anywhere
  • JavaScript — Browser apps, Node.js servers
  • Shared code — Validation, types, business logic

Native Performance

fib(35) benchmark — verified with hyperfine

Lux
28.1ms
C
29.0ms
Rust
41.2ms
Zig
47.0ms

Zero-cost effects via evidence passing — O(1) handler lookup

Built-in Effects

Everything you need, ready to use

Console

print, readLine, readInt

File

read, write, exists, listDir

Http

get, post, put, delete

HttpServer

listen, respond, routing

Sql

query, execute, transactions

Process

exec, env, args, exit

Random

int, float, bool

Time

now, sleep

Developer Experience

Modern tooling, built-in

# Package manager
lux pkg init myproject
lux pkg add json-parser
lux pkg install

# Development tools
lux fmt          # Format code
lux check        # Type check
lux test         # Run tests
lux watch app.lux # Hot reload

# LSP for your editor
lux lsp          # Start language server

Everything included:

  • Package Manager — Git repos, local paths, registry
  • LSP — VS Code, Neovim, Emacs, Helix
  • REPL — Interactive exploration
  • Debugger — Step through code
  • Formatter — Consistent style
  • Test Runner — Built-in test effect

Get Started

# Install via Nix (recommended)
nix run github:luxlang/lux

# Or build from source
git clone https://github.com/luxlang/lux
cd lux && nix develop
cargo build --release

# Start the REPL
./target/release/lux

# Run a file
./target/release/lux hello.lux

# Compile to native binary
./target/release/lux compile app.lux --run
// hello.lux
fn main(): Unit with {Console} = {
  Console.print("Hello, Lux!")
}

run main() with {}

Why Lux?

vs Haskell

Algebraic effects instead of monads. Same power, clearer code. Weeks to learn, not months.

vs Rust

No borrow checker to fight. Automatic memory management. Still native performance.

vs Go

Real type safety. Pattern matching. No nil panics. Effects track what code does.

vs TypeScript

Sound type system. Native compilation. Effects are tracked, not invisible.

vs Elm

Compiles to native, not just JS. Server-side, CLI apps, anywhere.

vs Zig

Higher-level abstractions. Algebraic types. Still fast.