Files
lux/README.md
Brandon Lucas 7e76acab18 feat: rebuild website with full learning funnel
Website rebuilt from scratch based on analysis of 11 beloved language
websites (Elm, Zig, Gleam, Swift, Kotlin, Haskell, OCaml, Crystal, Roc,
Rust, Go).

New website structure:
- Homepage with hero, playground, three pillars, install guide
- Language Tour with interactive lessons (hello world, types, effects)
- Examples cookbook with categorized sidebar
- API documentation index
- Installation guide (Nix and source)
- Sleek/noble design (black/gold, serif typography)

Also includes:
- New stdlib/json.lux module for JSON serialization
- Enhanced stdlib/http.lux with middleware and routing
- New string functions (charAt, indexOf, lastIndexOf, repeat)
- LSP improvements (rename, signature help, formatting)
- Package manager transitive dependency resolution
- Updated documentation for effects and stdlib
- New showcase example (task_manager.lux)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-16 23:05:35 -05:00

243 lines
5.9 KiB
Markdown

# Lux
A functional programming language with first-class effects, schema evolution, and behavioral types.
## Vision
Most programming languages treat three critical concerns as afterthoughts:
1. **Effects** — What can this code do? (Hidden, untraceable, untestable)
2. **Data Evolution** — Types change, data persists. (Manual migrations, runtime failures)
3. **Behavioral Properties** — Is this idempotent? Does it terminate? (Comments and hope)
Lux makes these first-class language features. The compiler knows what your code does, how your data evolves, and what properties your functions guarantee.
## Core Principles
### 1. Effects Are Explicit and Composable
```lux
fn fetchUser(id: UserId): User with {Database, Http} =
let profile = Http.get("/users/{id}")
let prefs = Database.query(userPrefsQuery(id))
User.merge(profile, prefs)
-- Testing: swap real effects for mocks
test "fetchUser returns merged data" =
run fetchUser(testId) with {
Database = mockDb({ testId: testPrefs }),
Http = mockHttp({ "/users/{testId}": testProfile })
}
|> Assert.eq(expectedUser)
```
No hidden side effects. No dependency injection boilerplate. Effects are declared, handlers are swappable, composition just works.
### 2. Schema Evolution Is Built-In
```lux
type User @v1 {
name: String,
email: String
}
type User @v2 {
name: String,
email: String,
age: Option<Int> -- optional field: auto-compatible
}
type User @v3 {
fullName: String, -- renamed: requires migration
email: String,
age: Option<Int>,
from @v2 = { fullName: v2.name, ..v2 }
}
```
The compiler tracks compatibility. Breaking changes are compile errors. Migrations are code, not config.
### 3. Behavioral Types Are First-Class
```lux
fn retry<F, T>(action: F): Result<T, Error>
where F: fn() -> T with {Fail},
where F is idempotent -- enforced!
=
match action() {
Ok(v) => Ok(v),
Err(_) => action() -- safe: we know it's idempotent
}
fn sort<T: Ord>(list: List<T>): List<T>
is pure,
is total,
where result.len == list.len,
where result.isSorted
```
Properties like `pure`, `total`, `idempotent`, `commutative` are part of the type system. The compiler proves what it can, tests what it can't.
## Example
```lux
-- Define an effect
effect Logger {
fn log(level: Level, msg: String): Unit
}
-- Define a versioned type
type Config @v1 {
host: String,
port: Int
}
type Config @v2 {
host: String,
port: Int,
timeout: Duration,
from @v1 = { timeout: Duration.seconds(30), ..v1 }
}
-- A function with explicit effects and properties
fn loadConfig(path: Path): Config @v2 with {FileSystem, Logger}
is total
=
Logger.log(Info, "Loading config from {path}")
let raw = FileSystem.read(path)
Config.parse(raw)
-- Run with handlers
fn main(): Unit with {Console} =
let config = run loadConfig("./config.json") with {
FileSystem = realFs,
Logger = consoleLogger
}
Console.print("Loaded: {config}")
```
## Status
**Core Language:** Complete
- Full type system with Hindley-Milner inference
- Pattern matching with exhaustiveness checking
- Algebraic data types, generics, string interpolation
- Effect system with handlers
- Behavioral types (pure, total, idempotent, deterministic, commutative)
- Schema evolution with version tracking
**Compilation Targets:**
- Interpreter (full-featured)
- C backend (functions, closures, pattern matching, lists, reference counting)
- JavaScript backend (full language, browser & Node.js, DOM, TEA runtime)
**Tooling:**
- REPL with history
- LSP server (diagnostics, hover, completions, go-to-definition)
- Formatter (`lux fmt`)
- Package manager (`lux pkg`)
- Watch mode / hot reload
**Standard Library:**
- String, List, Option, Result, Math, JSON modules
- Console, File, Http, Random, Time, Process effects
- SQL effect (SQLite with transactions)
- PostgreSQL effect (connection pooling ready)
- DOM effect (40+ browser operations)
See:
- [docs/ROADMAP.md](./docs/ROADMAP.md) — Development roadmap and feature status
- [docs/OVERVIEW.md](./docs/OVERVIEW.md) — Use cases, pros/cons, complexity analysis
## Design Goals
| Goal | Approach |
|------|----------|
| **Correctness by default** | Effects, schemas, and behaviors are compiler-checked |
| **Incremental adoption** | Start simple, add properties/versions as needed |
| **Zero-cost abstractions** | Effect handlers inline, versions compile away |
| **Practical, not academic** | Familiar syntax, clear errors, gradual verification |
## Non-Goals
- Not a systems language (no manual memory management)
- Not a scripting language (static types required)
- Not a proof assistant (verification is practical, not total)
## Building
### With Nix (recommended)
```bash
# Build
nix build
# Run the REPL
nix run
# Enter development shell
nix develop
# Run tests
nix develop --command cargo test
```
### With Cargo
Requires Rust 1.70+:
```bash
cargo build --release
./target/release/lux # REPL
./target/release/lux file.lux # Run a file
cargo test # Tests
```
## Examples
See the `examples/` directory:
- `hello.lux` — Hello World with effects
- `factorial.lux` — Recursive functions
- `effects.lux` — Custom effects and handlers
- `datatypes.lux` — ADTs and pattern matching
- `functional.lux` — Higher-order functions and pipes
### Quick REPL Session
```
$ cargo run
Lux v0.1.0
Type :help for help, :quit to exit
lux> let x = 42
lux> x * 2
84
lux> fn double(n: Int): Int = n * 2
lux> double(21)
42
lux> [1, 2, 3] |> List.reverse
[3, 2, 1]
lux> List.map([1, 2, 3], double)
[2, 4, 6]
lux> String.split("a,b,c", ",")
["a", "b", "c"]
lux> Some(42) |> Option.map(double)
Some(84)
lux> :quit
```
## Contributing
This project is in early design. Contributions welcome in:
- Language design discussions (open an issue)
- Syntax bikeshedding
- Semantic formalization
- Compiler implementation (once design stabilizes)
## License
MIT