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.3 KiB
6.3 KiB
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
import lib/math
fn main(): Unit with {Console} = {
Console.print(toString(lib/math.square(5)))
}
Wait, that's verbose. Use an alias:
Aliased Import
import lib/math as math
fn main(): Unit with {Console} = {
Console.print(toString(math.square(5)))
}
Selective Import
Import specific items directly:
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:
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:
// 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:
// 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:
// 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:
import std/prelude.* // Common utilities
import std/option // Option helpers
import std/result // Result helpers
import std/io // I/O utilities
std/prelude
import std/prelude.*
identity(42) // 42
compose(f, g) // Function composition
not(true) // false
and(true, false) // false
or(true, false) // true
std/option
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
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:
// 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
// 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
// Only export what others need
pub fn publicApi(): Result = ...
// Keep helpers private
fn internalHelper(): Int = ...
3. Use Aliases for Clarity
// 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
// 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 - Handling failures gracefully.