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>
6.7 KiB
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 intonext/n- Step overcontinue/c- Continueprint <expr>- Evaluate expressionbreak <line>- Set breakpointquit/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:
- Build something: See Tutorials
- Read the reference: See Language Reference
- Explore effects: See Effects Cookbook
- Join the community: GitHub discussions
Happy coding with Lux!