7.3 KiB
Lux: Vision and Roadmap
The Problems Lux Solves
1. The "What Can This Code Do?" Problem
In most languages, you can't tell from a function signature what it might do:
// TypeScript - what does this do? No idea without reading the code.
function processOrder(order: Order): Receipt { ... }
Could it hit a database? Send emails? Log? Throw? You don't know until you read every line (and every function it calls).
Lux solution:
fn processOrder(order: Order): Receipt with {Database, Email, Logger, Fail}
The signature is the documentation. Code review becomes "should this function really send emails?" Effects are compile-time checked.
2. The Testing Problem
Testing side-effecting code requires mocking frameworks, dependency injection containers, and boilerplate:
// TypeScript - need DI framework, mock libraries, setup/teardown
const mockDb = jest.mock('./database');
const mockEmail = jest.mock('./email');
// ... 50 lines of setup
Lux solution:
// Production
run processOrder(order) with {
Database = postgres(connString),
Email = sendgrid(apiKey),
Logger = cloudWatch
}
// Test - same code, different handlers
run processOrder(order) with {
Database = inMemoryDb(testData),
Email = collectEmails(sentList), // captures instead of sends
Logger = nullLogger
}
No mocking library. No DI framework. Just swap handlers.
3. The Schema Evolution Problem (Planned)
Types change. Data persists. Every production system eventually faces:
- "I renamed this field, now deserialization breaks"
- "I added a required field, old data can't load"
- "I need to migrate 10M rows and pray"
Lux solution:
type User @v1 { name: String, email: String }
type User @v2 {
name: String,
email: String,
createdAt: Timestamp,
from @v1 = { createdAt: Timestamp.epoch(), ..v1 } // migration
}
type User @v3 {
fullName: String, // renamed
email: String,
createdAt: Timestamp,
from @v2 = { fullName: v2.name, ..v2 }
}
// Compiler knows: v1 → v2 is auto-compatible, v2 → v3 needs migration
// Serialization handles any version automatically
4. The "Is This Safe?" Problem (Planned)
Critical properties are documented in comments and hoped for:
// IMPORTANT: This function must be idempotent for retry logic!
function chargeCard(payment: Payment): Result { ... }
Lux solution:
fn chargeCard(payment: Payment): Result
is idempotent // Compiler enforces or generates property tests
fn retry<F>(action: F, times: Int): Result
where F is idempotent // Won't compile if you pass non-idempotent function
What's Built vs. What's Needed
Currently Working (Phase 1: Core Language)
| Feature | Status | Notes |
|---|---|---|
| Lexer/Parser | Done | Full syntax support |
| Type Inference | Done | Hindley-Milner |
| Functions/Closures | Done | First-class functions |
| Pattern Matching | Done | Destructuring, guards |
| Records/Tuples/Lists | Done | Basic data structures |
| Effect Declarations | Done | effect Name { ... } |
| Effect Operations | Done | Effect.operation() |
| Effect Handlers | Done | handler name: Effect { ... } |
| Run with Handlers | Done | run expr with { ... } |
| Built-in Console/Fail | Done | Basic IO |
| REPL | Done | Interactive development |
| Type Checking | Done | With effect tracking |
Needed for Real Use (Phase 2: Practical)
| Feature | Effort | Why It Matters |
|---|---|---|
| Module System | 2-3 weeks | Can't build real apps without imports |
| Standard Library | Done | List.map, String.split, Option.map, etc. |
| File/Network Effects | 1-2 weeks | Real IO beyond Console |
| Better Error Messages | 2-3 weeks | Elm-quality diagnostics |
| JS/WASM Compilation | 4-6 weeks | Deploy to browsers/servers |
Needed for Full Vision (Phase 3: Differentiation)
| Feature | Effort | Why It Matters |
|---|---|---|
| Schema Evolution | 4-6 weeks | The versioned types system |
| Behavioral Types | 4-6 weeks | is pure, is idempotent, etc. |
| Effect Tracing/Debugging | 2-3 weeks | Elm-like debugging |
| LSP Server | 3-4 weeks | IDE support |
| Package Manager | 2-3 weeks | Share code |
Elm-Style Debugging for Effects
Elm's debugging is famous because:
- Time-travel: See app state at any point
- No runtime crashes: Everything is Result/Maybe
- Amazing error messages: Context, suggestions, examples
Lux can go further because effects are explicit:
Effect Tracing
Every effect operation can be automatically logged:
// With tracing enabled:
run processOrder(order) with {
Database = traced(postgres), // Logs all queries
Email = traced(sendgrid), // Logs all sends
Logger = traced(cloudWatch) // Meta-logging!
}
// Output:
// [00:00:01] Database.query("SELECT * FROM users WHERE id = 42")
// [00:00:02] Database.query("SELECT * FROM inventory WHERE sku = 'ABC'")
// [00:00:03] Email.send(to: "customer@example.com", subject: "Order Confirmed")
// [00:00:03] Logger.log(level: "info", msg: "Order 123 processed")
Effect Replay
Since all effects are captured, we can replay:
// Record effects during production
let recording = record(processOrder(order)) with { Database = postgres, ... }
// Replay in development with exact same effect responses
replay(recording) with { Database = mockFromRecording(recording) }
State Snapshots
Since state changes only happen through effects:
// Snapshot state before/after each effect
run debugSession(app) with {
State = snapshotted(initialState), // Captures every state change
Console = traced(stdout)
}
// Later: inspect state at any point, step forward/backward
Error Messages (To Build)
Current:
Type error at 15-45: Cannot unify Int with String
Goal (Elm-style):
── TYPE MISMATCH ─────────────────────────────────────── src/order.lux
The `calculateTotal` function expects an `Int` but got a `String`:
15│ let total = calculateTotal(order.quantity)
^^^^^^^^^^^^^^
`order.quantity` is a `String` but `calculateTotal` needs an `Int`.
Hint: Maybe you need to parse the string?
let qty = Int.parse(order.quantity)?
let total = calculateTotal(qty)
Development Effort Summary
To be minimally useful for real projects:
- Module system + standard library + better errors
- Estimate: 6-8 weeks of focused work
To deliver the full vision (effects + schemas + behavioral types):
- All of the above + schema evolution + behavioral types + compilation
- Estimate: 4-6 months of focused work
To have Elm-quality experience:
- All of the above + debugging tools + LSP + package manager
- Estimate: 8-12 months of focused work
Immediate Next Steps
Standard Library- Done! List, String, Option, Result operations- Module System -
import,export, namespaces - File Effect -
FileSystem.read,FileSystem.write - Error Message Overhaul - Source snippets, suggestions, colors
- JavaScript Backend - Compile to runnable JS
These would make Lux usable for small real projects.