Comparison
Lumina borrows deliberately from languages most developers already know, and leaves behind the parts that cause the most bugs. This page shows what came from where, and where those languages are still ahead.
What comes from where
| From | Lumina takes | Lumina leaves behind |
|---|---|---|
| TypeScript | function, name: Type, => lambdas, interface, template strings, T?, ??, array methods | any, null and undefined, types that vanish at runtime, untyped catch, ambient I/O, non-exhaustive switch, a JavaScript runtime |
| Go | one tool for build, test, format and packages; quick turnaround; lightweight tasks with spawn; channels with select and timeouts | nil, (value, err) pairs that can be ignored, interfaces as the only abstraction |
| Python | run a file directly, a REPL, low ceremony, inferred locals | dynamic typing, indentation as syntax, exceptions as the main error channel |
| Rust | T!E with ?, enums with payloads, exhaustive match, moves, borrowed views that cannot outlive their source, native code via LLVM | lifetime annotations, & versus &mut everywhere, Box/Rc/Arc choices, Send/Sync bounds, .clone() and .unwrap() noise, unsafe as the FFI story (Lumina uses @Trusted) |
And from none of them: capabilities. A function that touches the file system, the network or the clock receives that power as an ordinary parameter. The same mechanism is the effect system, the dependency-injection container and the test-double mechanism (see Capabilities).
One example: a fallible lookup
// TypeScript: nothing forces the caller to handle undefined
const name = findUser(id)?.name ?? "guest";
// Lumina: TypeScript's look, Rust's guarantee
name = findUser(id)?.name ?? "guest"
Because findUser returns User?, the missing case is part of the signature. Reading .name straight off a User? without unwrapping is a compile error in progress. Go needs four lines and lets you drop the error with _; Rust is safe but noisier. See Errors & Types.
Why not just use...
TypeScript? You keep the syntax, and the types stop being advisory. Node runs your JavaScript on a single thread, and error handling is ad hoc: anything can throw, and catch hands you an untyped value. Lumina checks types for real before the program runs: pass a String where an [Integer] is expected and the compiler stops with E4002, where plain JavaScript runs and prints garbage. You also get enforced exhaustive matching, errors in the type signature, effects in the parameter list, and native code with no Node runtime.
Go? Go's channels are the model for Lumina's, and the everyday patterns carry over: buffered channels, close, select with timeouts, deadlock reporting, worker pools and fan-in (see examples/channels_*.lumina and Async). You keep spawn, channels and the one-tool experience, and gain sum types, Optional instead of nil, generics with interface bounds, and purity checking. Several channel corners are still being rounded out: range-style for v in ch, rendezvous channels, telling "closed" from a value, select send cases, directional Sender<T>/Receiver<T> types, and failing loudly on a send to a closed channel in progress. Spawned tasks spread over all cores as goroutines do in progress.
Fast turnaround is a goal Lumina shares with Go, and Go sets the bar. On small and mid-sized programs Lumina is in Go's range; large programs are not measured yet mostly done.
Rust? Rust is excellent at what it promises: strong guarantees and fast code. It is also chatty. Even plain business logic asks you to spell out lifetimes, & versus &mut, Box/Rc/Arc, Send/Sync, and a sprinkle of .clone() and .unwrap(). Each detail is reasonable alone, but together they fill your working memory with plumbing instead of the problem, and the code is hard to read for anyone who is not a Rust specialist. Lumina keeps Result, ?, exhaustive match and move semantics and drops that layer: memory is garbage-collected by default, and deterministic or allocation-free code is something you opt into per function (see Memory). Diagnostics are shorter than rustc's, with a line number on every error in progress and a source excerpt with a caret under the offending line (see Built for Agents).
Python? You keep "write a file, run it", and gain static types, native speed, and mistakes caught before the program runs.
Where they are still ahead
Being honest about this matters more than a feature table:
- All four are shipped, stable and have huge ecosystems. Lumina's package ecosystem is close to empty, and the language is still moving.
- Go runs goroutines preemptively on a battle-tested scheduler. Lumina's tasks are cooperative in progress, and stack safety still has edge cases to close mostly done.
- Rust gives global zero-cost, no-GC and data-race-freedom guarantees. Lumina is safe by default but may fall back to the GC, has runtime panics and index faults, and its compile-time sharing checks are still growing in progress.
- Python stays terser, and TypeScript's conditional and mapped types have no Lumina counterpart.
What is actually new
No single feature here is unprecedented. The bet is the combination: effects visible in ordinary parameter lists, the same mechanism doubling as dependency injection, test fakes and a security boundary the package tool can audit (lumina package verify), typed LLM calls as a compiler-checked effect (see Interop & Stdlib), all behind syntax a TypeScript developer can read on day one.
Next: Status & Roadmap