- SKILLS.md: Update roadmap phases with actual completion status - Phase 0-1 complete, Phase 2-5 partial, resolved design decisions - OVERVIEW.md: Add HttpServer, Test effect, JIT to completed features - ROADMAP.md: Add HttpServer, Process, Test effects to done list - VISION.md: Update Phase 2-3 tables with current status - guide/05-effects.md: Add Time, HttpServer, Test to effects table - guide/09-stdlib.md: Add HttpServer, Time, Test effect docs - reference/syntax.md: Fix interpolation syntax, remove unsupported literals - testing.md: Add native Test effect documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
258 lines
7.6 KiB
Markdown
258 lines
7.6 KiB
Markdown
# 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
|
|
// 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:**
|
|
|
|
```lux
|
|
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
|
|
// TypeScript - need DI framework, mock libraries, setup/teardown
|
|
const mockDb = jest.mock('./database');
|
|
const mockEmail = jest.mock('./email');
|
|
// ... 50 lines of setup
|
|
```
|
|
|
|
**Lux solution:**
|
|
|
|
```lux
|
|
// 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:**
|
|
|
|
```lux
|
|
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:
|
|
|
|
```typescript
|
|
// IMPORTANT: This function must be idempotent for retry logic!
|
|
function chargeCard(payment: Payment): Result { ... }
|
|
```
|
|
|
|
**Lux solution:**
|
|
|
|
```lux
|
|
fn chargeCard(payment: Payment): Result
|
|
is idempotent // Compiler enforces or generates property tests
|
|
```
|
|
|
|
```lux
|
|
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 |
|
|
|
|
### Completed (Phase 2: Practical)
|
|
|
|
| Feature | Status | Notes |
|
|
|---------|--------|-------|
|
|
| **Module System** | ✅ Done | Imports, exports, aliases, selective imports |
|
|
| **Standard Library** | ✅ Done | List, String, Option, Result, Math, Json modules |
|
|
| **File Effect** | ✅ Done | read, write, exists, delete, listDir, mkdir |
|
|
| **HTTP Client Effect** | ✅ Done | get, post, put, delete |
|
|
| **HTTP Server Effect** | ✅ Done | listen, accept, respond, stop |
|
|
| **Process Effect** | ✅ Done | exec, env, args, cwd, exit |
|
|
| **Random/Time Effects** | ✅ Done | int, float, bool, now, sleep |
|
|
| **JIT Compiler** | ✅ Partial | Numeric code ~160x faster, strings/ADTs pending |
|
|
| **Error Messages** | ✅ Partial | Context lines shown, suggestions improving |
|
|
|
|
### In Progress (Phase 3: Differentiation)
|
|
|
|
| Feature | Status | Notes |
|
|
|---------|--------|-------|
|
|
| **Schema Evolution** | ⚠️ Partial | Parsing done, type integration pending |
|
|
| **Behavioral Types** | ⚠️ Partial | Parsing done, verification pending |
|
|
| **LSP Server** | ✅ Done | Diagnostics, hover, completions working |
|
|
| **Package Manager** | ⚠️ Partial | Manifest parsing exists |
|
|
| **Effect Tracing** | Planned | Elm-like debugging |
|
|
|
|
---
|
|
|
|
## Elm-Style Debugging for Effects
|
|
|
|
Elm's debugging is famous because:
|
|
1. **Time-travel**: See app state at any point
|
|
2. **No runtime crashes**: Everything is Result/Maybe
|
|
3. **Amazing error messages**: Context, suggestions, examples
|
|
|
|
Lux can go further because effects are explicit:
|
|
|
|
### Effect Tracing
|
|
|
|
Every effect operation can be automatically logged:
|
|
|
|
```lux
|
|
// 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:
|
|
|
|
```lux
|
|
// 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:
|
|
|
|
```lux
|
|
// 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
|
|
|
|
1. ~~**Standard Library**~~ - Done! List, String, Option, Result operations
|
|
2. ~~**Module System**~~ - Done! Imports, exports, aliases, selective imports
|
|
3. **File Effect** - `FileSystem.read`, `FileSystem.write`
|
|
4. **Error Message Overhaul** - Source snippets, suggestions, colors
|
|
5. **JavaScript Backend** - Compile to runnable JS
|
|
|
|
These would make Lux usable for small real projects.
|