Files
lux/docs/COMPILATION_STRATEGY.md
Brandon Lucas 33b4f57faf fix: C backend String functions, record type aliases, docs cleanup
- Add String.fromChar, chars, substring, toUpper, toLower, replace,
  startsWith, endsWith, join to C backend
- Fix record type alias unification by adding expand_type_alias and
  unify_with_env functions
- Update docs to reflect current implementation status
- Clean up outdated roadmap items and fix inconsistencies
- Add comprehensive language comparison document

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

336 lines
8.9 KiB
Markdown

# Lux Compilation Strategy
## Vision
Lux should compile to native code with zero-cost effects AND compile to JavaScript for frontend development - all while eliminating runtime overhead like Svelte does.
## Current State
| Component | Status |
|-----------|--------|
| Interpreter | Full-featured, all language constructs |
| C Backend | Complete (functions, closures, pattern matching, lists, RC) |
| JS Backend | Complete (full language, browser & Node.js, DOM, TEA) |
| JIT (Cranelift) | Integer arithmetic only, ~160x speedup |
| Targets | Native (via C), JavaScript, JIT |
## Target Architecture
```
┌──→ C Backend ──→ GCC/Clang ──→ Native Binary (servers, CLI)
Lux Source ──→ HIR ──→ MIR ──→ JS Backend ──→ Optimized JS (no runtime, like Svelte)
└──→ Wasm Backend ──→ WebAssembly (browsers, edge)
```
## Compilation Targets Comparison
### What Other Languages Do
| Language | Backend | Runtime | Effect Strategy |
|----------|---------|---------|-----------------|
| **Koka** | C (Perceus) | None | Evidence passing |
| **Elm** | JS | Small runtime | No effects (pure) |
| **Gleam** | Erlang/JS | BEAM/JS runtime | No effects |
| **Svelte** | JS | **No runtime** | Compile-time reactivity |
| **ReScript** | JS | Minimal | No effects |
| **PureScript** | JS | Small runtime | Monad transformers |
### Key Insight: Svelte's No-Runtime Approach
Svelte compiles components to vanilla JavaScript that directly manipulates the DOM. No virtual DOM diffing, no framework code shipped to the browser.
```svelte
<!-- Svelte component -->
<script>
let count = 0;
</script>
<button on:click={() => count++}>{count}</button>
```
Compiles to:
```javascript
// Direct DOM manipulation, no runtime
function create_fragment(ctx) {
let button;
return {
c() {
button = element("button");
button.textContent = ctx[0];
},
m(target, anchor) {
insert(target, button, anchor);
listen(button, "click", ctx[1]);
},
p(ctx, dirty) {
if (dirty & 1) set_data(button, ctx[0]);
}
};
}
```
**We can do the same for Lux effects on the frontend.**
## Phase 1: C Backend (Native Compilation)
### Goal
Compile Lux to C code that can be compiled by GCC/Clang for native execution.
### Why C?
1. **Portability**: C compilers exist for every platform
2. **Performance**: Leverage decades of GCC/Clang optimizations
3. **No runtime**: Like Koka, compile effects away
4. **Proven path**: Koka, Nim, Chicken Scheme all do this successfully
### Implementation Plan
#### Step 1.1: Basic C Code Generation
- Integer arithmetic and comparisons
- Function definitions and calls
- If/else expressions
- Let bindings
#### Step 1.2: Data Structures
- Records → C structs
- ADTs → Tagged unions
- Lists → Linked lists or arrays
- Strings → UTF-8 byte arrays
#### Step 1.3: Effect Compilation (Evidence Passing)
Transform:
```lux
fn greet(name: String): Unit with {Console} =
Console.print("Hello, " + name)
```
To:
```c
void greet(Evidence* ev, LuxString name) {
LuxString msg = lux_string_concat("Hello, ", name);
ev->console->print(ev, msg);
}
```
#### Step 1.4: Memory Management (Perceus-style)
- Compile-time reference counting insertion
- Reuse analysis for in-place updates
- No garbage collector needed
### File Structure
```
src/
codegen/
mod.rs # Code generation module
c_backend.rs # C code generation
c_runtime.h # Minimal C runtime header
c_runtime.c # Runtime support (RC, strings, etc.)
```
## Phase 2: JavaScript Backend (Frontend)
### Goal
Compile Lux to optimized JavaScript with NO runtime, like Svelte.
### Effect Mapping
| Lux Effect | JS Compilation |
|------------|----------------|
| `Console.print` | `console.log()` |
| `Dom.getElementById` | `document.getElementById()` |
| `Dom.addEventListener` | Direct event binding |
| `Http.get` | `fetch()` with async/await |
| `State.get/set` | Compile-time reactivity |
### No-Runtime Strategy
Instead of shipping a runtime, compile effects to direct DOM manipulation:
```lux
effect Dom {
fn getElementById(id: String): Element
fn setTextContent(el: Element, text: String): Unit
fn addEventListener(el: Element, event: String, handler: fn(): Unit): Unit
}
fn counter(): Unit with {Dom} = {
let btn = Dom.getElementById("btn")
let count = 0
Dom.addEventListener(btn, "click", fn() => {
count = count + 1
Dom.setTextContent(btn, toString(count))
})
}
```
Compiles to:
```javascript
// No runtime needed - direct DOM calls
function counter() {
const btn = document.getElementById("btn");
let count = 0;
btn.addEventListener("click", () => {
count = count + 1;
btn.textContent = String(count);
});
}
```
### Reactive State (Like Svelte)
For reactive state, compile to fine-grained updates:
```lux
effect Reactive {
fn signal<T>(initial: T): Signal<T>
fn derived<T>(compute: fn(): T): Signal<T>
}
let count = Reactive.signal(0)
let doubled = Reactive.derived(fn() => count.get() * 2)
```
Compiles to:
```javascript
// Compile-time reactivity, no virtual DOM
let count = 0;
let doubled;
const count_subscribers = new Set();
function set_count(value) {
count = value;
doubled = count * 2; // Statically known dependency
count_subscribers.forEach(fn => fn());
}
```
## Phase 3: WebAssembly Backend
### Goal
Compile to Wasm for browser and edge deployment.
### Strategy
- Reuse C backend: `C → Emscripten → Wasm`
- Or direct Wasm generation via Cranelift
## Phase 4: Zero-Cost Effects (Evidence Passing)
### Current Problem
Effects use runtime handler lookup - O(n) per effect operation.
### Solution: Evidence Passing
Transform effect operations to direct function calls at compile time.
Before (runtime lookup):
```
performEffect("Console", "print", [msg])
→ search handler stack
→ invoke handler
```
After (compile-time):
```
evidence.console.print(msg)
→ direct function call
```
### Implementation
```rust
// Transform AST to evidence-passing form
fn transform_to_evidence(expr: Expr, effects: &[Effect]) -> Expr {
match expr {
Expr::EffectOp { effect, operation, args } => {
// Transform Console.print(x) to ev.console.print(x)
Expr::Call {
func: Expr::Field {
object: Expr::Field {
object: Expr::Var("__evidence__"),
field: effect.to_lowercase(),
},
field: operation,
},
args,
}
}
// ... recurse on other expressions
}
}
```
## Phase 5: FBIP (Functional But In-Place)
### Goal
Detect when functional updates can be performed in-place.
### Example
```lux
fn increment(tree: Tree): Tree =
match tree {
Leaf(n) => Leaf(n + 1),
Node(l, r) => Node(increment(l), increment(r))
}
```
With FBIP analysis, if `tree` has refcount 1, reuse the memory:
```c
Tree increment(Tree tree) {
if (tree->tag == LEAF) {
if (is_unique(tree)) {
tree->leaf.value += 1; // In-place!
return tree;
} else {
return make_leaf(tree->leaf.value + 1);
}
}
// ...
}
```
## Performance Targets
| Metric | Target | How |
|--------|--------|-----|
| Effect overhead | 0% | Evidence passing |
| Memory allocation | Minimal | Perceus RC + FBIP |
| JS bundle size | No runtime | Direct compilation |
| Native vs C | <5% overhead | Good C codegen |
## Milestones
### C Backend - COMPLETE
- [x] Integer/bool expressions → C
- [x] Functions → C functions
- [x] If/else → C conditionals
- [x] Let bindings → C variables
- [x] Basic main() generation
- [x] Build with GCC/Clang
- [x] Strings → C strings
- [x] Pattern matching → Switch/if chains
- [x] Lists → Linked structures
- [x] Closures
- [x] Reference counting (lists, boxed values)
### JavaScript Backend - COMPLETE
- [x] Basic expressions → JS
- [x] Functions → JS functions
- [x] Effects → Direct DOM/API calls
- [x] Standard library (String, List, Option, Result, Math, JSON)
- [x] DOM effect (40+ operations)
- [x] Html module (type-safe HTML)
- [x] TEA runtime (Elm Architecture)
- [x] Browser & Node.js support
### Remaining Work
- [ ] Evidence passing for zero-cost effects
- [ ] FBIP (Functional But In-Place) optimization
- [ ] WASM backend (deprioritized)
## References
- [Koka: Evidence Passing](https://github.com/koka-lang/koka)
- [Perceus: Garbage Free Reference Counting](https://www.microsoft.com/en-us/research/publication/perceus-garbage-free-reference-counting-with-reuse/)
- [FBIP: Functional But In-Place](https://www.microsoft.com/en-us/research/publication/fp2-fully-in-place-functional-programming/)
- [Svelte: No Virtual DOM](https://svelte.dev/blog/virtual-dom-is-pure-overhead)
- [Eff: Efficient Compilation of Handlers](https://dl.acm.org/doi/10.1145/3485479)