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>
This commit is contained in:
272
docs/guide/03-functions.md
Normal file
272
docs/guide/03-functions.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# Chapter 3: Functions
|
||||
|
||||
Functions are the building blocks of Lux programs. They're first-class values—you can pass them around, return them, and store them in data structures.
|
||||
|
||||
## Defining Functions
|
||||
|
||||
Basic syntax:
|
||||
|
||||
```lux
|
||||
fn name(param1: Type1, param2: Type2): ReturnType = body
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```lux
|
||||
fn add(a: Int, b: Int): Int = a + b
|
||||
|
||||
fn greet(name: String): String = "Hello, " + name
|
||||
|
||||
fn isEven(n: Int): Bool = n % 2 == 0
|
||||
```
|
||||
|
||||
## Single Expression vs Block Body
|
||||
|
||||
Simple functions use `=`:
|
||||
|
||||
```lux
|
||||
fn square(x: Int): Int = x * x
|
||||
```
|
||||
|
||||
Complex functions use `= { ... }`:
|
||||
|
||||
```lux
|
||||
fn classify(n: Int): String = {
|
||||
let abs_n = if n < 0 then -n else n
|
||||
if abs_n == 0 then "zero"
|
||||
else if abs_n < 10 then "small"
|
||||
else "large"
|
||||
}
|
||||
```
|
||||
|
||||
The last expression in a block is the return value. No `return` keyword needed.
|
||||
|
||||
## Type Inference
|
||||
|
||||
Return types can often be inferred:
|
||||
|
||||
```lux
|
||||
fn add(a: Int, b: Int) = a + b // Returns Int
|
||||
fn not(b: Bool) = !b // Returns Bool
|
||||
```
|
||||
|
||||
But parameter types are always required:
|
||||
|
||||
```lux
|
||||
fn double(x) = x * 2 // Error: parameter type required
|
||||
```
|
||||
|
||||
## Anonymous Functions (Lambdas)
|
||||
|
||||
Functions without names:
|
||||
|
||||
```lux
|
||||
fn(x: Int): Int => x * 2
|
||||
```
|
||||
|
||||
Used with higher-order functions:
|
||||
|
||||
```lux
|
||||
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]
|
||||
```
|
||||
|
||||
Store in variables:
|
||||
|
||||
```lux
|
||||
let double = fn(x: Int): Int => x * 2
|
||||
double(5) // 10
|
||||
```
|
||||
|
||||
## Higher-Order Functions
|
||||
|
||||
Functions that take or return functions:
|
||||
|
||||
```lux
|
||||
// Takes a function
|
||||
fn apply(f: fn(Int): Int, x: Int): Int = f(x)
|
||||
|
||||
apply(fn(x: Int): Int => x + 1, 5) // 6
|
||||
|
||||
// Returns a function
|
||||
fn makeAdder(n: Int): fn(Int): Int =
|
||||
fn(x: Int): Int => x + n
|
||||
|
||||
let add10 = makeAdder(10)
|
||||
add10(5) // 15
|
||||
add10(20) // 30
|
||||
```
|
||||
|
||||
## Closures
|
||||
|
||||
Functions capture their environment:
|
||||
|
||||
```lux
|
||||
fn counter(): fn(): Int with {State} = {
|
||||
let count = 0
|
||||
fn(): Int with {State} => {
|
||||
State.put(State.get() + 1)
|
||||
State.get()
|
||||
}
|
||||
}
|
||||
|
||||
// The returned function remembers `count`
|
||||
```
|
||||
|
||||
More practical example:
|
||||
|
||||
```lux
|
||||
fn makeMultiplier(factor: Int): fn(Int): Int =
|
||||
fn(x: Int): Int => x * factor
|
||||
|
||||
let triple = makeMultiplier(3)
|
||||
triple(4) // 12
|
||||
triple(7) // 21
|
||||
```
|
||||
|
||||
## Recursion
|
||||
|
||||
Functions can call themselves:
|
||||
|
||||
```lux
|
||||
fn factorial(n: Int): Int =
|
||||
if n <= 1 then 1
|
||||
else n * factorial(n - 1)
|
||||
|
||||
factorial(5) // 120
|
||||
```
|
||||
|
||||
## Tail Call Optimization
|
||||
|
||||
Lux optimizes tail-recursive functions:
|
||||
|
||||
```lux
|
||||
// Not tail-recursive (stack grows)
|
||||
fn factorial(n: Int): Int =
|
||||
if n <= 1 then 1
|
||||
else n * factorial(n - 1) // Must multiply AFTER recursive call
|
||||
|
||||
// Tail-recursive (constant stack)
|
||||
fn factorialTail(n: Int, acc: Int): Int =
|
||||
if n <= 1 then acc
|
||||
else factorialTail(n - 1, n * acc) // Recursive call is LAST operation
|
||||
|
||||
fn factorial(n: Int): Int = factorialTail(n, 1)
|
||||
```
|
||||
|
||||
The tail-recursive version won't overflow the stack.
|
||||
|
||||
## Function Composition
|
||||
|
||||
Combine functions:
|
||||
|
||||
```lux
|
||||
fn compose<A, B, C>(f: fn(B): C, g: fn(A): B): fn(A): C =
|
||||
fn(x: A): C => f(g(x))
|
||||
|
||||
fn addOne(x: Int): Int = x + 1
|
||||
fn double(x: Int): Int = x * 2
|
||||
|
||||
let addOneThenDouble = compose(double, addOne)
|
||||
addOneThenDouble(5) // 12 = (5 + 1) * 2
|
||||
```
|
||||
|
||||
## Partial Application
|
||||
|
||||
Create new functions by fixing some arguments:
|
||||
|
||||
```lux
|
||||
fn add(a: Int, b: Int): Int = a + b
|
||||
|
||||
// Manually partial apply
|
||||
fn add5(b: Int): Int = add(5, b)
|
||||
|
||||
add5(3) // 8
|
||||
```
|
||||
|
||||
## Pipeline Style
|
||||
|
||||
Chain operations readably:
|
||||
|
||||
```lux
|
||||
// Without pipeline
|
||||
toString(List.sum(List.map(List.filter([1,2,3,4,5], isEven), square)))
|
||||
|
||||
// With intermediate variables
|
||||
let nums = [1, 2, 3, 4, 5]
|
||||
let evens = List.filter(nums, isEven)
|
||||
let squared = List.map(evens, square)
|
||||
let total = List.sum(squared)
|
||||
toString(total)
|
||||
```
|
||||
|
||||
## Functions with Effects
|
||||
|
||||
Functions that perform effects declare them:
|
||||
|
||||
```lux
|
||||
fn pureAdd(a: Int, b: Int): Int = a + b // No effects
|
||||
|
||||
fn printAdd(a: Int, b: Int): Unit with {Console} = {
|
||||
let sum = a + b
|
||||
Console.print("Sum: " + toString(sum))
|
||||
}
|
||||
```
|
||||
|
||||
Effects propagate:
|
||||
|
||||
```lux
|
||||
fn helper(): Int with {Console} = {
|
||||
Console.print("Computing...")
|
||||
42
|
||||
}
|
||||
|
||||
// Must also declare Console since it calls helper
|
||||
fn main(): Int with {Console} = {
|
||||
let x = helper()
|
||||
x * 2
|
||||
}
|
||||
```
|
||||
|
||||
## Generic Functions
|
||||
|
||||
Functions that work with any type:
|
||||
|
||||
```lux
|
||||
fn identity<T>(x: T): T = x
|
||||
|
||||
identity(42) // 42
|
||||
identity("hello") // "hello"
|
||||
identity(true) // true
|
||||
|
||||
fn pair<A, B>(a: A, b: B): (A, B) = (a, b)
|
||||
|
||||
pair(1, "one") // (1, "one")
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
```lux
|
||||
// Basic function
|
||||
fn name(param: Type): Return = body
|
||||
|
||||
// Lambda
|
||||
fn(x: Int): Int => x * 2
|
||||
|
||||
// Higher-order (takes function)
|
||||
fn apply(f: fn(Int): Int, x: Int): Int = f(x)
|
||||
|
||||
// Higher-order (returns function)
|
||||
fn makeAdder(n: Int): fn(Int): Int = fn(x: Int): Int => x + n
|
||||
|
||||
// Generic
|
||||
fn identity<T>(x: T): T = x
|
||||
|
||||
// With effects
|
||||
fn greet(name: String): Unit with {Console} = Console.print("Hi " + name)
|
||||
```
|
||||
|
||||
## Next
|
||||
|
||||
[Chapter 4: Data Types](04-data-types.md) - Algebraic data types and pattern matching.
|
||||
Reference in New Issue
Block a user