# 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.