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:
320
docs/guide/07-modules.md
Normal file
320
docs/guide/07-modules.md
Normal file
@@ -0,0 +1,320 @@
|
||||
# Chapter 7: Modules
|
||||
|
||||
As programs grow, you need to split code across files. Lux has a module system for organizing and sharing code.
|
||||
|
||||
## Module Basics
|
||||
|
||||
Every `.lux` file is a module. The file path determines the module path:
|
||||
|
||||
```
|
||||
project/
|
||||
├── main.lux # Module: main
|
||||
├── utils.lux # Module: utils
|
||||
└── lib/
|
||||
├── math.lux # Module: lib/math
|
||||
└── strings.lux # Module: lib/strings
|
||||
```
|
||||
|
||||
## Importing Modules
|
||||
|
||||
### Basic Import
|
||||
|
||||
```lux
|
||||
import lib/math
|
||||
|
||||
fn main(): Unit with {Console} = {
|
||||
Console.print(toString(lib/math.square(5)))
|
||||
}
|
||||
```
|
||||
|
||||
Wait, that's verbose. Use an alias:
|
||||
|
||||
### Aliased Import
|
||||
|
||||
```lux
|
||||
import lib/math as math
|
||||
|
||||
fn main(): Unit with {Console} = {
|
||||
Console.print(toString(math.square(5)))
|
||||
}
|
||||
```
|
||||
|
||||
### Selective Import
|
||||
|
||||
Import specific items directly:
|
||||
|
||||
```lux
|
||||
import lib/math.{square, cube}
|
||||
|
||||
fn main(): Unit with {Console} = {
|
||||
Console.print(toString(square(5))) // No prefix needed
|
||||
Console.print(toString(cube(3)))
|
||||
}
|
||||
```
|
||||
|
||||
### Wildcard Import
|
||||
|
||||
Import everything:
|
||||
|
||||
```lux
|
||||
import lib/math.*
|
||||
|
||||
fn main(): Unit with {Console} = {
|
||||
Console.print(toString(square(5)))
|
||||
Console.print(toString(cube(3)))
|
||||
Console.print(toString(factorial(6)))
|
||||
}
|
||||
```
|
||||
|
||||
Use sparingly—it can cause name conflicts.
|
||||
|
||||
## Visibility
|
||||
|
||||
By default, declarations are private. Use `pub` to export:
|
||||
|
||||
```lux
|
||||
// lib/math.lux
|
||||
|
||||
// Public - can be imported
|
||||
pub fn square(x: Int): Int = x * x
|
||||
|
||||
pub fn cube(x: Int): Int = x * x * x
|
||||
|
||||
// Private - internal helper
|
||||
fn helper(x: Int): Int = x + 1
|
||||
|
||||
// Public type
|
||||
pub type Point = { x: Int, y: Int }
|
||||
```
|
||||
|
||||
## Creating a Module
|
||||
|
||||
Let's create a string utilities module:
|
||||
|
||||
```lux
|
||||
// lib/strings.lux
|
||||
|
||||
/// Repeat a string n times
|
||||
pub fn repeat(s: String, n: Int): String =
|
||||
if n <= 0 then ""
|
||||
else s + repeat(s, n - 1)
|
||||
|
||||
/// Check if string starts with prefix
|
||||
pub fn startsWith(s: String, prefix: String): Bool =
|
||||
String.startsWith(s, prefix)
|
||||
|
||||
/// Check if string ends with suffix
|
||||
pub fn endsWith(s: String, suffix: String): Bool =
|
||||
String.endsWith(s, suffix)
|
||||
|
||||
/// Pad string on the left to reach target length
|
||||
pub fn padLeft(s: String, length: Int, char: String): String = {
|
||||
let current = String.length(s)
|
||||
if current >= length then s
|
||||
else padLeft(char + s, length, char)
|
||||
}
|
||||
|
||||
/// Pad string on the right to reach target length
|
||||
pub fn padRight(s: String, length: Int, char: String): String = {
|
||||
let current = String.length(s)
|
||||
if current >= length then s
|
||||
else padRight(s + char, length, char)
|
||||
}
|
||||
```
|
||||
|
||||
Using it:
|
||||
|
||||
```lux
|
||||
// main.lux
|
||||
import lib/strings as str
|
||||
|
||||
fn main(): Unit with {Console} = {
|
||||
Console.print(str.repeat("ab", 3)) // "ababab"
|
||||
Console.print(str.padLeft("5", 3, "0")) // "005"
|
||||
}
|
||||
|
||||
let output = run main() with {}
|
||||
```
|
||||
|
||||
## Module Organization Patterns
|
||||
|
||||
### Feature Modules
|
||||
|
||||
Group by feature:
|
||||
|
||||
```
|
||||
project/
|
||||
├── main.lux
|
||||
├── users/
|
||||
│ ├── types.lux # User type definitions
|
||||
│ ├── repository.lux # Database operations
|
||||
│ └── service.lux # Business logic
|
||||
├── orders/
|
||||
│ ├── types.lux
|
||||
│ ├── repository.lux
|
||||
│ └── service.lux
|
||||
└── shared/
|
||||
├── utils.lux
|
||||
└── effects.lux
|
||||
```
|
||||
|
||||
### Layer Modules
|
||||
|
||||
Group by layer:
|
||||
|
||||
```
|
||||
project/
|
||||
├── main.lux
|
||||
├── domain/ # Business logic (pure)
|
||||
│ ├── user.lux
|
||||
│ └── order.lux
|
||||
├── effects/ # Effect definitions
|
||||
│ ├── database.lux
|
||||
│ └── email.lux
|
||||
├── handlers/ # Effect implementations
|
||||
│ ├── postgres.lux
|
||||
│ └── smtp.lux
|
||||
└── api/ # Entry points
|
||||
└── http.lux
|
||||
```
|
||||
|
||||
## Standard Library
|
||||
|
||||
Lux has a standard library in the `std/` directory:
|
||||
|
||||
```lux
|
||||
import std/prelude.* // Common utilities
|
||||
import std/option // Option helpers
|
||||
import std/result // Result helpers
|
||||
import std/io // I/O utilities
|
||||
```
|
||||
|
||||
### std/prelude
|
||||
|
||||
```lux
|
||||
import std/prelude.*
|
||||
|
||||
identity(42) // 42
|
||||
compose(f, g) // Function composition
|
||||
not(true) // false
|
||||
and(true, false) // false
|
||||
or(true, false) // true
|
||||
```
|
||||
|
||||
### std/option
|
||||
|
||||
```lux
|
||||
import std/option as opt
|
||||
|
||||
opt.some(42) // Some(42)
|
||||
opt.none() // None
|
||||
opt.map(Some(5), double) // Some(10)
|
||||
opt.flatMap(Some(5), safeDivide)
|
||||
opt.unwrapOr(None, 0) // 0
|
||||
```
|
||||
|
||||
### std/result
|
||||
|
||||
```lux
|
||||
import std/result as res
|
||||
|
||||
res.ok(42) // Ok(42)
|
||||
res.err("oops") // Err("oops")
|
||||
res.mapOk(Ok(5), double) // Ok(10)
|
||||
res.mapErr(Err("x"), upper) // Err("X")
|
||||
```
|
||||
|
||||
## Circular Dependencies
|
||||
|
||||
Lux detects circular imports:
|
||||
|
||||
```lux
|
||||
// a.lux
|
||||
import b
|
||||
pub fn fromA(): Int = b.fromB() + 1
|
||||
|
||||
// b.lux
|
||||
import a
|
||||
pub fn fromB(): Int = a.fromA() + 1
|
||||
|
||||
// Error: Circular dependency detected
|
||||
```
|
||||
|
||||
Solution: extract shared code to a third module.
|
||||
|
||||
## Module Best Practices
|
||||
|
||||
### 1. One Concept Per Module
|
||||
|
||||
```lux
|
||||
// Good: focused module
|
||||
// user.lux - User type and operations
|
||||
pub type User = { id: Int, name: String }
|
||||
pub fn createUser(name: String): User = ...
|
||||
pub fn validateUser(u: User): Bool = ...
|
||||
|
||||
// Bad: kitchen sink
|
||||
// utils.lux - random stuff
|
||||
pub fn parseUser(s: String): User = ...
|
||||
pub fn formatDate(d: Date): String = ...
|
||||
pub fn calculateTax(amount: Int): Int = ...
|
||||
```
|
||||
|
||||
### 2. Export Deliberately
|
||||
|
||||
```lux
|
||||
// Only export what others need
|
||||
pub fn publicApi(): Result = ...
|
||||
|
||||
// Keep helpers private
|
||||
fn internalHelper(): Int = ...
|
||||
```
|
||||
|
||||
### 3. Use Aliases for Clarity
|
||||
|
||||
```lux
|
||||
// Clear what comes from where
|
||||
import database/postgres as db
|
||||
import cache/redis as cache
|
||||
|
||||
fn getData(id: Int): Data with {Database, Cache} = {
|
||||
match cache.get(id) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
let d = db.query(id)
|
||||
cache.set(id, d)
|
||||
d
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Group Related Imports
|
||||
|
||||
```lux
|
||||
// Standard library
|
||||
import std/prelude.*
|
||||
import std/option as opt
|
||||
|
||||
// Project modules
|
||||
import lib/database as db
|
||||
import lib/cache as cache
|
||||
|
||||
// Local modules
|
||||
import ./types.{User, Order}
|
||||
import ./validation
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
| Syntax | Meaning |
|
||||
|--------|---------|
|
||||
| `import path/to/module` | Import module |
|
||||
| `import path/to/module as alias` | Import with alias |
|
||||
| `import path/to/module.{a, b}` | Import specific items |
|
||||
| `import path/to/module.*` | Import all exports |
|
||||
| `pub fn` / `pub type` | Export declaration |
|
||||
|
||||
## Next
|
||||
|
||||
[Chapter 8: Error Handling](08-errors.md) - Handling failures gracefully.
|
||||
Reference in New Issue
Block a user