Files
lux/docs/guide/10-advanced.md
Brandon Lucas 44f88afcf8 docs: add comprehensive language documentation
Documentation structure inspired by Rust Book, Elm Guide, and others:

Guide (10 chapters):
- Introduction and setup
- Basic types (Int, String, Bool, List, Option, Result)
- Functions (closures, higher-order, composition)
- Data types (ADTs, pattern matching, records)
- Effects (the core innovation)
- Handlers (patterns and techniques)
- Modules (imports, exports, organization)
- Error handling (Fail, Option, Result)
- Standard library reference
- Advanced topics (traits, generics, optimization)

Reference:
- Complete syntax reference

Tutorials:
- Calculator (parsing, evaluation, REPL)
- Dependency injection (testing with effects)
- Project ideas (16 projects by difficulty)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 17:43:41 -05:00

6.7 KiB

Chapter 10: Advanced Topics

This chapter covers advanced features for building larger applications.

Traits

Traits define shared behavior across types:

trait Show {
    fn show(self): String
}

impl Show for Int {
    fn show(self): String = toString(self)
}

impl Show for Bool {
    fn show(self): String = if self then "true" else "false"
}

impl Show for List<T> where T: Show {
    fn show(self): String = {
        let items = List.map(self, fn(x: T): String => x.show())
        "[" + String.join(items, ", ") + "]"
    }
}

Using traits:

fn display<T>(value: T): Unit with {Console} where T: Show =
    Console.print(value.show())

display(42)           // "42"
display(true)         // "true"
display([1, 2, 3])    // "[1, 2, 3]"

Generic Types

Types with parameters:

type Pair<A, B> =
    | MkPair(A, B)

fn first<A, B>(p: Pair<A, B>): A =
    match p {
        MkPair(a, _) => a
    }

fn second<A, B>(p: Pair<A, B>): B =
    match p {
        MkPair(_, b) => b
    }

let p = MkPair(1, "one")
first(p)   // 1
second(p)  // "one"

Type Constraints

Restrict generic types:

fn maximum<T>(list: List<T>): Option<T> where T: Ord = {
    match list {
        [] => None,
        [x] => Some(x),
        [x, ...rest] => {
            match maximum(rest) {
                None => Some(x),
                Some(y) => Some(if x > y then x else y)
            }
        }
    }
}

Tail Call Optimization

Lux optimizes tail-recursive functions:

// Not tail-recursive - stack grows with each call
fn sumBad(n: Int): Int =
    if n <= 0 then 0
    else n + sumBad(n - 1)  // Addition happens AFTER recursive call

// Tail-recursive - constant stack space
fn sumGood(n: Int, acc: Int): Int =
    if n <= 0 then acc
    else sumGood(n - 1, acc + n)  // Recursive call is the LAST operation

fn sum(n: Int): Int = sumGood(n, 0)

The compiler transforms tail calls into loops, preventing stack overflow.

Effect Polymorphism

Functions can be polymorphic over effects:

fn withLogging<E>(action: fn(): Int with {E}): Int with {E, Console} = {
    Console.print("Starting action")
    let result = action()
    Console.print("Action returned: " + toString(result))
    result
}

// Works with any effect set
fn pureAction(): Int = 42
fn randomAction(): Int with {Random} = Random.int(1, 100)

withLogging(pureAction)     // Works
withLogging(randomAction)   // Works

Behavioral Properties

Annotate functions with properties:

// Pure function - no effects
fn add(a: Int, b: Int): Int is pure = a + b

// Total function - always terminates
fn factorial(n: Int): Int is total =
    if n <= 1 then 1 else n * factorial(n - 1)

// Idempotent - same result if called multiple times
fn setConfig(key: String, value: String): Unit with {State} is idempotent =
    State.put(value)

These are currently documentation, but future versions may verify them.

Documentation Comments

Use /// for documentation:

/// Calculates the factorial of a non-negative integer.
///
/// # Arguments
/// * `n` - A non-negative integer
///
/// # Returns
/// The factorial of n (n!)
///
/// # Example
/// ```
/// factorial(5)  // Returns 120
/// ```
pub fn factorial(n: Int): Int =
    if n <= 1 then 1 else n * factorial(n - 1)

Performance Tips

1. Use Tail Recursion

// Slow - builds up stack
fn lengthSlow<T>(list: List<T>): Int =
    match list {
        [] => 0,
        [_, ...rest] => 1 + lengthSlow(rest)
    }

// Fast - constant stack
fn lengthFast<T>(list: List<T>, acc: Int): Int =
    match list {
        [] => acc,
        [_, ...rest] => lengthFast(rest, acc + 1)
    }

2. Avoid Repeated Concatenation

// Slow - O(n²)
fn buildStringSlow(n: Int): String =
    if n <= 0 then ""
    else buildStringSlow(n - 1) + "x"

// Fast - use List.join
fn buildStringFast(n: Int): String =
    String.join(List.map(List.range(0, n), fn(_: Int): String => "x"), "")

3. Use Built-in Functions

// Slow - manual implementation
fn sumManual(nums: List<Int>): Int =
    match nums {
        [] => 0,
        [x, ...rest] => x + sumManual(rest)
    }

// Fast - built-in fold
fn sumBuiltin(nums: List<Int>): Int =
    List.fold(nums, 0, fn(acc: Int, x: Int): Int => acc + x)

4. JIT Compilation

For performance-critical numeric code, the JIT compiler provides ~160x speedup:

// In Rust code, use the JIT compiler
let mut jit = JitCompiler::new().unwrap();
jit.compile_function(&func).unwrap();
let result = unsafe { jit.call_function("fib", &[30]).unwrap() };

Debugging

Debug Printing

fn debug<T>(label: String, value: T): T with {Console} = {
    Console.print(label + ": " + toString(value))
    value
}

fn process(x: Int): Int with {Console} = {
    let step1 = debug("step1", x * 2)
    let step2 = debug("step2", step1 + 10)
    step2
}

The Debugger

Run with debugger:

lux --debug program.lux

Commands:

  • step / s - Step into
  • next / n - Step over
  • continue / c - Continue
  • print <expr> - Evaluate expression
  • break <line> - Set breakpoint
  • quit / q - Exit

Effect Tracing

fn traced<E>(action: fn(): T with {E}): T with {E, Console} = {
    Console.print(">>> Entering action")
    let result = action()
    Console.print("<<< Exiting with: " + toString(result))
    result
}

IDE Support

Lux has LSP support for:

  • VS Code: Install Lux extension
  • Neovim: Configure with nvim-lspconfig

Features:

  • Syntax highlighting
  • Error diagnostics
  • Go to definition
  • Hover for types
  • Auto-completion

Start LSP server:

lux --lsp

Project Structure

Recommended layout for larger projects:

my-project/
├── lux.toml           # Project manifest
├── src/
│   ├── main.lux       # Entry point
│   ├── lib.lux        # Library code
│   └── modules/
│       ├── users.lux
│       └── orders.lux
├── std/               # Custom std extensions
├── tests/
│   ├── users_test.lux
│   └── orders_test.lux
└── examples/
    └── demo.lux

Summary

Feature Syntax
Trait trait Name { fn method(self): T }
Impl impl Trait for Type { ... }
Generic fn f<T>(x: T): T
Constraint where T: Trait
Tail recursion Last expression is recursive call
Doc comment /// Documentation

What's Next?

You now know Lux! Try:

  1. Build something: See Tutorials
  2. Read the reference: See Language Reference
  3. Explore effects: See Effects Cookbook
  4. Join the community: GitHub discussions

Happy coding with Lux!