Files
lux/docs/OVERVIEW.md
Brandon Lucas 705bd57e81 docs: add demos and update documentation for new features
Documentation:
- Update IMPLEMENTATION_PLAN.md with current status (222 tests)
- Update feature comparison table (Schema Evolution, Behavioral Types: )
- Add HttpServer to built-in effects list
- Update OVERVIEW.md with working behavioral types examples

Demo Programs:
- examples/schema_evolution.lux - Version annotations, constraints, runtime ops
- examples/behavioral_types.lux - pure, deterministic, commutative, idempotent, total

Sample Project:
- projects/rest-api/ - Full REST API demo with:
  - Task CRUD endpoints
  - Pattern matching router
  - JSON serialization
  - Effect-tracked request handling

Tests:
- Add behavioral type tests (pure, deterministic, commutative, idempotent, total)
- 227 tests passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 22:14:55 -05:00

10 KiB

Lux Language Overview

What is Lux?

Lux is a statically-typed functional programming language with algebraic effects as a first-class feature. It makes side effects explicit, trackable, and testable.

What Can You Do With It?

Currently Working

// Functions with type inference
fn factorial(n: Int): Int =
    if n <= 1 then 1 else n * factorial(n - 1)

// Higher-order functions
fn apply(f: fn(Int): Int, x: Int): Int = f(x)
fn double(x: Int): Int = x * 2
let result = apply(double, 21)  // 42

// Lambdas and closures
let add = fn(a: Int, b: Int): Int => a + b
let addFive = fn(x: Int): Int => add(5, x)

// Pattern matching
fn describe(n: Int): String =
    match n {
        0 => "zero",
        1 => "one",
        _ => "many"
    }

// Records
let person = { name: "Alice", age: 30 }
let age = person.age

// Tuples
let point = (10, 20)

// Lists
let numbers = [1, 2, 3, 4, 5]

// Pipe operator
let result = 5 |> double |> addOne  // (5 * 2) + 1 = 11

// Built-in effects (Console, Fail)
Console.print("Hello, world!")

// Custom effects
effect Logger {
    fn log(level: String, msg: String): Unit
}

// Effect handlers
handler consoleLogger: Logger {
    fn log(level, msg) = Console.print("[" + level + "] " + msg)
}

// Running with handlers
fn greet(name: String): Unit with {Logger} =
    Logger.log("info", "Hello, " + name)

run greet("Alice") with { Logger = consoleLogger }

Standard Library (Built-in)

// List operations
List.map([1, 2, 3], fn(x: Int): Int => x * 2)    // [2, 4, 6]
List.filter([1, 2, 3, 4], fn(x: Int): Bool => x > 2)  // [3, 4]
List.fold([1, 2, 3], 0, fn(acc: Int, x: Int): Int => acc + x)  // 6
List.head([1, 2, 3])      // Some(1)
List.tail([1, 2, 3])      // Some([2, 3])
List.concat([1, 2], [3])  // [1, 2, 3]
List.reverse([1, 2, 3])   // [3, 2, 1]
List.length([1, 2, 3])    // 3
List.get([1, 2, 3], 0)    // Some(1)
List.range(0, 5)          // [0, 1, 2, 3, 4]

// String operations
String.split("a,b,c", ",")       // ["a", "b", "c"]
String.join(["a", "b"], "-")     // "a-b"
String.trim("  hello  ")         // "hello"
String.contains("hello", "ell")  // true
String.replace("hi", "i", "ey")  // "hey"
String.length("hello")           // 5
String.chars("hi")               // ['h', 'i']
String.lines("a\nb")             // ["a", "b"]

// Option operations
let x = Some(42)
let y = None
Option.map(x, fn(n: Int): Int => n * 2)  // Some(84)
Option.flatMap(x, fn(n: Int): Option<Int> => Some(n + 1))  // Some(43)
Option.getOrElse(y, 0)  // 0
Option.isSome(x)        // true
Option.isNone(y)        // true

// Result operations
let ok = Ok(42)
let err = Err("failed")
Result.map(ok, fn(n: Int): Int => n * 2)  // Ok(84)
Result.getOrElse(err, 0)  // 0
Result.isOk(ok)           // true
Result.isErr(err)         // true

// Utility functions
print("Hello")      // prints to stdout
toString(42)        // "42"
typeOf([1, 2, 3])   // "List"

Also Working

// Generic type parameters
fn map<T, U>(f: fn(T): U, list: List<T>): List<U> = ...
fn identity<T>(x: T): T = x

// String interpolation
let name = "Alice"
Console.print("Hello, {name}!")  // Hello, Alice!
Console.print("1 + 2 = {1 + 2}") // 1 + 2 = 3

// File effects
let content = File.read("config.txt")
File.write("output.txt", "Hello!")
let exists = File.exists("file.lux")

// HTTP client effects
let response = Http.get("https://api.example.com/data")
Http.post("https://api.example.com/submit", "{\"key\": \"value\"}")

// HTTP server effects
HttpServer.listen(8080)
let req = HttpServer.accept()  // Returns { method, path, body, headers }
HttpServer.respond(200, "Hello, World!")
HttpServer.stop()

// Random effects
let n = Random.int(1, 100)
let coin = Random.bool()

// Time effects
let now = Time.now()
Time.sleep(1000)  // milliseconds

// JSON parsing
let obj = Json.parse("{\"name\": \"Alice\"}")
let str = Json.stringify(obj)

// Module system
import mymodule
import utils/helpers as h
import math.{sqrt, abs}
import prelude.*

Also Working

// Behavioral types with compile-time verification
fn factorial(n: Int): Int is pure is deterministic is total =
    if n <= 1 then 1 else n * factorial(n - 1)

fn add(a: Int, b: Int): Int is commutative = a + b

fn absolute(x: Int): Int is idempotent =
    if x < 0 then 0 - x else x

// Schema evolution with version tracking
fn processV2(data: Int @v2): Int = data * 2
let value: Int @v2 = 42
let result = processV2(value)

// Version constraints
fn processModern(x: Int @v2+): Int = x  // v2 or later
fn processAny(x: Int @latest): Int = x  // any version

Planned (Not Yet Fully Implemented)

  • Full Compilation: JIT works for numeric code, strings/lists/ADTs missing
  • Auto-migration Generation: Migration bodies stored, execution pending

Primary Use Cases

1. Learning Effect Systems

Lux is an excellent educational tool for understanding algebraic effects without the complexity of Haskell's monad transformers or the academic syntax of languages like Koka.

2. Testable Application Code

Effects make dependencies explicit. Swap handlers for testing:

// Production
run app() with { Database = postgres, Http = realHttp }

// Testing
run app() with { Database = mockDb, Http = mockHttp }

3. Domain Modeling

Explicit effects document what code can do:

fn processOrder(order: Order): Receipt with {Database, Email, Logger}
// ^ The signature tells you exactly what side effects this function performs

4. Prototyping

Quick iteration with type inference and a REPL.


Pros and Cons

Pros

Advantage Description
Explicit Effects Function signatures show what side effects are possible
Testability Swap effect handlers for mocking—no dependency injection frameworks
Type Safety Static types catch errors at compile time
Type Inference Write less type annotations, compiler figures it out
Clean Syntax ML-family inspired, minimal boilerplate
Pattern Matching Destructure data elegantly
Immutable by Default Easier to reason about
REPL Interactive development

Cons

Limitation Description
Limited JIT Cranelift JIT works for numeric code only
No Package Manager Can't share/publish packages yet
New Paradigm Effects require learning new concepts
Small Ecosystem No community packages yet
Early Stage Bugs likely, features incomplete

Complexity Assessment

Conceptual Complexity

Concept Difficulty Notes
Basic syntax Easy Similar to other ML-family languages
Functions Easy Standard functional style
Pattern matching Easy If you know any FP language
Type system Medium Hindley-Milner inference helps
Effects Medium New concept, but simpler than monads
Handlers Medium Requires understanding of continuations

Comparison to Other Languages

Language Complexity Comparison to Lux
Python Simpler No types, no effect tracking
TypeScript Similar Lux has effects, TS has larger ecosystem
Elm Similar Both pure FP, Lux has general effects
Haskell More Complex Monads harder than algebraic effects
Koka Similar Koka more academic, Lux more practical syntax
Rust More Complex Ownership adds significant complexity

Learning Curve

Beginner (1-2 hours):

  • Basic expressions, functions, let bindings
  • If/else, pattern matching
  • REPL usage

Intermediate (1-2 days):

  • Custom types and records
  • Higher-order functions
  • Built-in effects (Console)

Advanced (1 week):

  • Custom effect definitions
  • Effect handlers
  • Understanding when to use effects vs. regular functions

When to Use Lux

Good Fit

  • Learning algebraic effects
  • Prototyping with explicit effect tracking
  • Small tools where testability matters
  • Teaching functional programming concepts

Not a Good Fit (Yet)

  • Large production applications (early stage)
  • Performance-critical code (JIT limited to numeric)
  • Web frontend development (no JS compilation)
  • Systems programming (no low-level control)

Example Session

$ cargo run
Lux v0.1.0
Type :help for help, :quit to exit

lux> let x = 42
lux> x * 2
84
lux> fn greet(name: String): Unit with {Console} = Console.print("Hello, " + name)
lux> greet("World")
Hello, World
()
lux> let nums = [1, 2, 3]
lux> nums
[1, 2, 3]
lux> :quit

Architecture

Source Code
    │
    ▼
┌─────────┐
│  Lexer  │  → Tokens
└─────────┘
    │
    ▼
┌─────────┐
│ Parser  │  → AST
└─────────┘
    │
    ▼
┌─────────────┐
│ Type Checker│  → Typed AST + Effect Tracking
└─────────────┘
    │
    ├─────────────────────────┐
    ▼                         ▼
┌─────────────┐       ┌──────────────┐
│ Interpreter │       │ JIT Compiler │
│ (default)   │       │ (Cranelift)  │
└─────────────┘       └──────────────┘
    │                         │
    ▼                         ▼
Values + Effects        Native Code
                       (~160x speedup)

Future Roadmap

Complete:

  • Standard Library (List, String, Option, Result, JSON)
  • Module System (imports, exports, aliases)
  • LSP Server (basic diagnostics, hover, completions)
  • Generics and String Interpolation
  • File/HTTP/Random/Time Effects

In Progress:

  1. Behavioral Type Verification - Total, idempotent, deterministic checking
  2. Schema Evolution - Type system integration, auto-migration
  3. Error Message Quality - Elm-style suggestions

Planned: 4. HTTP Server Effect - Build web APIs 5. SQL Effect - Database access 6. Package Manager - Share code 7. JavaScript Backend - Run in browsers