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:
2026-02-13 17:43:41 -05:00
parent 9ee7148d24
commit 44f88afcf8
16 changed files with 4845 additions and 0 deletions

125
docs/tutorials/README.md Normal file
View File

@@ -0,0 +1,125 @@
# Tutorials
Learn Lux by building real projects.
## Standard Programs
These tutorials cover common programming tasks:
| Tutorial | What You'll Build | Concepts |
|----------|-------------------|----------|
| [Calculator](calculator.md) | REPL calculator | Parsing, evaluation, REPL loop |
| [Todo App](todo.md) | CLI task manager | File I/O, data structures |
| [HTTP Client](http-client.md) | API consumer | HTTP effects, JSON parsing |
| [Word Counter](word-counter.md) | Text analyzer | File reading, string ops |
## Effect Showcases
These tutorials demonstrate Lux's unique effect system:
| Tutorial | What You'll Learn | Key Concept |
|----------|-------------------|-------------|
| [Dependency Injection](dependency-injection.md) | Testing with mock handlers | Handler swapping |
| [State Machines](state-machines.md) | Modeling state transitions | Custom effects |
| [Effects Cookbook](effects-cookbook.md) | Common effect patterns | Handler patterns |
## Quick Start: Your First Project
### 1. Create Project Directory
```bash
mkdir my-first-lux
cd my-first-lux
```
### 2. Create Main File
```lux
// main.lux
fn main(): Unit with {Console} = {
Console.print("Welcome to my Lux project!")
Console.print("Enter your name:")
let name = Console.readLine()
Console.print("Hello, " + name + "!")
}
let output = run main() with {}
```
### 3. Run It
```bash
lux main.lux
```
### 4. Add a Module
```lux
// lib/greetings.lux
pub fn hello(name: String): String =
"Hello, " + name + "!"
pub fn goodbye(name: String): String =
"Goodbye, " + name + "!"
```
```lux
// main.lux
import lib/greetings as greet
fn main(): Unit with {Console} = {
Console.print("Enter your name:")
let name = Console.readLine()
Console.print(greet.hello(name))
Console.print(greet.goodbye(name))
}
let output = run main() with {}
```
## Project Ideas by Difficulty
### Beginner
- **Temperature converter** - Convert between Celsius, Fahrenheit, Kelvin
- **Number guessing game** - Random number with hints
- **Simple quiz** - Multiple choice questions with scoring
- **Unit converter** - Length, weight, volume conversions
### Intermediate
- **Markdown previewer** - Parse basic Markdown to HTML
- **Contact book** - CRUD with file persistence
- **Simple grep** - Search files for patterns
- **CSV processor** - Read, filter, transform CSV files
### Advanced
- **Test framework** - Use effects for test isolation
- **Config loader** - Effect-based configuration with validation
- **Mini interpreter** - Build a small language
- **Chat client** - HTTP-based chat application
### Effect Showcases
- **Transaction system** - Rollback on failure
- **Capability security** - Effects as capabilities
- **Async simulation** - Model async with effects
- **Dependency graph** - Track and inject dependencies
## How to Use These Tutorials
1. **Read through first** - Understand the goal
2. **Type the code** - Don't copy-paste
3. **Experiment** - Modify and see what happens
4. **Build your own** - Apply concepts to your ideas
## Getting Help
- Check the [Language Reference](../reference/syntax.md)
- See [examples/](../../examples/) for working code
- Use the REPL to experiment
Happy building!

View File

@@ -0,0 +1,251 @@
# Tutorial: Building a Calculator
Build a REPL calculator that evaluates arithmetic expressions.
## What You'll Learn
- Defining algebraic data types
- Pattern matching
- Recursive functions
- REPL loops with effects
## The Goal
```
Calculator REPL
> 2 + 3
5
> (10 - 4) * 2
12
> 100 / 5 + 3
23
> quit
Goodbye!
```
## Step 1: Define the Expression Type
First, we model arithmetic expressions:
```lux
// calculator.lux
type Expr =
| Num(Int)
| Add(Expr, Expr)
| Sub(Expr, Expr)
| Mul(Expr, Expr)
| Div(Expr, Expr)
```
This is an *algebraic data type* (ADT). An `Expr` is one of:
- A number: `Num(42)`
- An addition: `Add(Num(2), Num(3))`
- And so on...
## Step 2: Evaluate Expressions
Now we evaluate expressions to integers:
```lux
fn eval(e: Expr): Result<Int, String> =
match e {
Num(n) => Ok(n),
Add(a, b) => evalBinOp(a, b, fn(x: Int, y: Int): Int => x + y),
Sub(a, b) => evalBinOp(a, b, fn(x: Int, y: Int): Int => x - y),
Mul(a, b) => evalBinOp(a, b, fn(x: Int, y: Int): Int => x * y),
Div(a, b) => {
match (eval(a), eval(b)) {
(Ok(x), Ok(0)) => Err("Division by zero"),
(Ok(x), Ok(y)) => Ok(x / y),
(Err(e), _) => Err(e),
(_, Err(e)) => Err(e)
}
}
}
fn evalBinOp(a: Expr, b: Expr, op: fn(Int, Int): Int): Result<Int, String> =
match (eval(a), eval(b)) {
(Ok(x), Ok(y)) => Ok(op(x, y)),
(Err(e), _) => Err(e),
(_, Err(e)) => Err(e)
}
```
Pattern matching makes this clear:
- `Num(n)` just returns the number
- Operations evaluate both sides, then apply the operator
- Division checks for zero
## Step 3: Parse Expressions
For simplicity, we'll parse a limited format. A real parser would be more complex.
```lux
fn parseSimple(input: String): Result<Expr, String> = {
let trimmed = String.trim(input)
// Try to parse as a number
if isNumber(trimmed) then
Ok(Num(parseInt(trimmed)))
else
// Try to find an operator
match findOperator(trimmed) {
Some((left, op, right)) => {
match (parseSimple(left), parseSimple(right)) {
(Ok(l), Ok(r)) => Ok(makeOp(l, op, r)),
(Err(e), _) => Err(e),
(_, Err(e)) => Err(e)
}
},
None => Err("Cannot parse: " + trimmed)
}
}
fn isNumber(s: String): Bool = {
let chars = String.chars(s)
List.all(chars, fn(c: String): Bool =>
c == "-" || (c >= "0" && c <= "9")
)
}
fn parseInt(s: String): Int = {
// Simplified - assumes valid integer
List.fold(String.chars(s), 0, fn(acc: Int, c: String): Int =>
if c == "-" then acc
else acc * 10 + charToDigit(c)
) * (if String.startsWith(s, "-") then -1 else 1)
}
fn charToDigit(c: String): Int =
match c {
"0" => 0, "1" => 1, "2" => 2, "3" => 3, "4" => 4,
"5" => 5, "6" => 6, "7" => 7, "8" => 8, "9" => 9,
_ => 0
}
fn findOperator(s: String): Option<(String, String, String)> = {
// Find last +/- at depth 0 (lowest precedence)
// Then *// (higher precedence)
// This is a simplified approach
let addSub = findOpAtDepth(s, ["+", "-"], 0)
match addSub {
Some(r) => Some(r),
None => findOpAtDepth(s, ["*", "/"], 0)
}
}
fn makeOp(left: Expr, op: String, right: Expr): Expr =
match op {
"+" => Add(left, right),
"-" => Sub(left, right),
"*" => Mul(left, right),
"/" => Div(left, right),
_ => Num(0) // Should not happen
}
```
## Step 4: The REPL Loop
Now we build the interactive loop:
```lux
fn repl(): Unit with {Console} = {
Console.print("Calculator REPL (type 'quit' to exit)")
replLoop()
}
fn replLoop(): Unit with {Console} = {
Console.print("> ")
let input = Console.readLine()
if input == "quit" then
Console.print("Goodbye!")
else {
match parseSimple(input) {
Ok(expr) => {
match eval(expr) {
Ok(result) => Console.print(toString(result)),
Err(e) => Console.print("Error: " + e)
}
},
Err(e) => Console.print("Parse error: " + e)
}
replLoop() // Continue the loop
}
}
fn main(): Unit with {Console} = repl()
let output = run main() with {}
```
## Step 5: Test It
```bash
$ lux calculator.lux
Calculator REPL (type 'quit' to exit)
> 2 + 3
5
> 10 - 4
6
> 6 * 7
42
> 100 / 0
Error: Division by zero
> quit
Goodbye!
```
## Extending the Calculator
Try adding:
1. **Parentheses**: Parse `(2 + 3) * 4`
2. **Variables**: Store results in named variables
3. **Functions**: `sqrt`, `abs`, `pow`
4. **History**: Use State effect to track previous results
### Adding Variables (Bonus)
```lux
effect Variables {
fn get(name: String): Option<Int>
fn set(name: String, value: Int): Unit
}
fn evalWithVars(e: Expr): Result<Int, String> with {Variables} =
match e {
Var(name) => {
match Variables.get(name) {
Some(v) => Ok(v),
None => Err("Unknown variable: " + name)
}
},
// ... other cases
}
handler memoryVars: Variables {
fn get(name) = resume(State.get(name))
fn set(name, value) = {
State.put(name, value)
resume(())
}
}
```
## Complete Code
See `examples/tutorials/calculator.lux` for the full implementation.
## What You Learned
- **ADTs** model structured data
- **Pattern matching** destructures data cleanly
- **Recursion** processes nested structures
- **Result** handles errors without exceptions
- **REPL loops** combine effects naturally
## Next Tutorial
[Todo App](todo.md) - Build a task manager with file persistence.

View File

@@ -0,0 +1,380 @@
# Tutorial: Dependency Injection with Effects
Learn how effects provide natural dependency injection for testing and flexibility.
## What You'll Learn
- Using effects for dependencies
- Swapping handlers for testing
- The "ports and adapters" pattern
## The Problem
Imagine you're building a user registration system:
```javascript
// Traditional approach (pseudo-code)
function registerUser(userData) {
const validated = validate(userData);
database.insert(validated); // Hard-coded dependency
emailService.send(validated.email, "Welcome!"); // Another one
logger.info("User registered"); // And another
return validated.id;
}
```
Testing this requires mocking `database`, `emailService`, and `logger`. In many languages, this means:
- Dependency injection frameworks
- Mock libraries
- Complex test setup
## The Lux Way
In Lux, dependencies are effects:
```lux
// Define what we need (not how it works)
effect Database {
fn insert(table: String, data: String): Int
fn query(sql: String): List<String>
}
effect Email {
fn send(to: String, subject: String, body: String): Unit
}
effect Logger {
fn info(message: String): Unit
fn error(message: String): Unit
}
```
Now our business logic declares its dependencies:
```lux
type User = { name: String, email: String }
fn registerUser(user: User): Int with {Database, Email, Logger} = {
Logger.info("Registering user: " + user.name)
// Validate
if user.name == "" then {
Logger.error("Empty name")
Fail.fail("Name required")
} else ()
if user.email == "" then {
Logger.error("Empty email")
Fail.fail("Email required")
} else ()
// Insert into database
let userId = Database.insert("users", userToJson(user))
Logger.info("Created user with ID: " + toString(userId))
// Send welcome email
Email.send(user.email, "Welcome!", "Thanks for registering, " + user.name)
Logger.info("Sent welcome email")
userId
}
fn userToJson(user: User): String =
"{\"name\":\"" + user.name + "\",\"email\":\"" + user.email + "\"}"
```
## Production Handlers
For production, we implement real handlers:
```lux
handler postgresDb: Database {
fn insert(table, data) = {
let result = Http.post("http://db-service/insert", data)
// Parse ID from response
resume(parseId(result))
}
fn query(sql) = {
let result = Http.post("http://db-service/query", sql)
resume(parseRows(result))
}
}
handler smtpEmail: Email {
fn send(to, subject, body) = {
Http.post("http://email-service/send", formatEmail(to, subject, body))
resume(())
}
}
handler consoleLogger: Logger {
fn info(msg) = {
Console.print("[INFO] " + msg)
resume(())
}
fn error(msg) = {
Console.print("[ERROR] " + msg)
resume(())
}
}
```
Production code:
```lux
fn main(): Unit with {Console, Http} = {
let user = User { name: "Alice", email: "alice@example.com" }
let result = run registerUser(user) with {
Database = postgresDb,
Email = smtpEmail,
Logger = consoleLogger
}
Console.print("Registered user ID: " + toString(result))
}
```
## Test Handlers
For testing, we swap in mock handlers:
```lux
// Mock database that stores in memory
handler mockDb: Database {
fn insert(table, data) = {
let id = State.get()
State.put(id + 1)
Console.print("[MOCK DB] Inserted into " + table + ": " + data)
resume(id)
}
fn query(sql) = {
Console.print("[MOCK DB] Query: " + sql)
resume(["mock", "data"])
}
}
// Mock email that just logs
handler mockEmail: Email {
fn send(to, subject, body) = {
Console.print("[MOCK EMAIL] To: " + to + ", Subject: " + subject)
resume(())
}
}
// Silent logger for tests
handler silentLogger: Logger {
fn info(msg) = resume(())
fn error(msg) = resume(())
}
// Collecting logger for assertions
handler collectingLogger: Logger {
fn info(msg) = {
State.put(State.get() + "[INFO] " + msg + "\n")
resume(())
}
fn error(msg) = {
State.put(State.get() + "[ERROR] " + msg + "\n")
resume(())
}
}
```
Test code:
```lux
fn testRegisterUser(): Unit with {Console} = {
let user = User { name: "Test", email: "test@test.com" }
// Run with mocks
let (userId, logs) = run {
let id = run {
run registerUser(user) with {
Database = mockDb,
Email = mockEmail,
Logger = collectingLogger
}
} with { State = 1 } // Database ID counter
let logs = State.get()
(id, logs)
} with { State = "" } // Log accumulator
// Assertions
Console.print("User ID: " + toString(userId))
Console.print("Logs:\n" + logs)
if userId == 1 then
Console.print("✓ User ID is correct")
else
Console.print("✗ User ID is wrong")
if String.contains(logs, "Registering user") then
Console.print("✓ Registration was logged")
else
Console.print("✗ Registration was not logged")
}
```
## Testing Failure Cases
Test error handling by using handlers that fail:
```lux
handler failingDb: Database {
fn insert(table, data) = {
Fail.fail("Database connection failed")
}
fn query(sql) = resume([])
}
fn testDatabaseFailure(): Unit with {Console} = {
let user = User { name: "Test", email: "test@test.com" }
let result = run {
run registerUser(user) with {
Database = failingDb,
Email = mockEmail,
Logger = silentLogger
}
} with {}
Console.print("Test: Database failure is handled")
// The Fail effect should have triggered
}
```
## The "Ports and Adapters" Pattern
This is also known as hexagonal architecture:
```
┌─────────────────────┐
│ Business Logic │
│ (Pure + Effects) │
└─────────────────────┘
┌───────────────┼───────────────┐
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ Database │ │ Email │ │ Logger │
│ Effect │ │ Effect │ │ Effect │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
┌─────────┼─────────┐ │ ┌─────┼─────┐
│ │ │ │ │ │
┌───┴───┐ ┌───┴───┐ │ ┌───┴───┐ ┌───┴───┐ ┌─────┴─────┐
│Postgres│ │MockDB │ │ │ SMTP │ │Console│ │ Collector │
└───────┘ └───────┘ │ └───────┘ └───────┘ └───────────┘
┌─────┴─────┐
│ MockEmail │
└───────────┘
```
- **Business logic** is pure (plus declared effects)
- **Effects** are the ports (interfaces)
- **Handlers** are the adapters (implementations)
## Benefits
1. **No mocking libraries needed** - Just write a different handler
2. **Type-safe** - The compiler ensures all effects are handled
3. **Explicit dependencies** - You can see what a function needs
4. **Easy to test** - Swap handlers, no reflection or magic
5. **Flexible** - Same code, different environments
## Complete Example
```lux
// main.lux
// === Effects (Ports) ===
effect Database {
fn insert(table: String, data: String): Int
}
effect Email {
fn send(to: String, body: String): Unit
}
// === Business Logic ===
type User = { name: String, email: String }
fn registerUser(user: User): Int with {Database, Email, Fail} = {
if user.name == "" then Fail.fail("Name required") else ()
let id = Database.insert("users", user.name)
Email.send(user.email, "Welcome!")
id
}
// === Production Handlers (Adapters) ===
handler prodDb: Database {
fn insert(table, data) = {
Console.print("[DB] INSERT INTO " + table)
resume(42) // Would be real ID
}
}
handler prodEmail: Email {
fn send(to, body) = {
Console.print("[EMAIL] Sending to " + to)
resume(())
}
}
// === Test Handlers ===
handler testDb: Database {
fn insert(table, data) = resume(1)
}
handler testEmail: Email {
fn send(to, body) = resume(())
}
// === Running ===
fn production(): Unit with {Console} = {
let user = User { name: "Alice", email: "alice@example.com" }
let id = run registerUser(user) with {
Database = prodDb,
Email = prodEmail
}
Console.print("Created user: " + toString(id))
}
fn test(): Unit with {Console} = {
let user = User { name: "Test", email: "test@test.com" }
let id = run registerUser(user) with {
Database = testDb,
Email = testEmail
}
if id == 1 then Console.print("✓ Test passed")
else Console.print("✗ Test failed")
}
fn main(): Unit with {Console} = {
Console.print("=== Production ===")
production()
Console.print("\n=== Test ===")
test()
}
let output = run main() with {}
```
## What You Learned
- Effects define **what** you need, handlers define **how**
- Swap handlers for testing without changing business logic
- No dependency injection framework needed
- Type system ensures all dependencies are satisfied
## Next Tutorial
[State Machines](state-machines.md) - Model state transitions with custom effects.

View File

@@ -0,0 +1,325 @@
# Project Ideas
Here are projects to build with Lux, organized by difficulty and purpose.
## Beginner Projects
### 1. Temperature Converter
Convert between Celsius, Fahrenheit, and Kelvin.
**Skills**: Basic I/O, functions, conditionals
```lux
// Starter code
fn celsiusToFahrenheit(c: Float): Float = c * 9.0 / 5.0 + 32.0
fn fahrenheitToCelsius(f: Float): Float = (f - 32.0) * 5.0 / 9.0
fn main(): Unit with {Console} = {
Console.print("Temperature Converter")
Console.print("1. Celsius to Fahrenheit")
Console.print("2. Fahrenheit to Celsius")
// ... implement menu and conversion
}
```
### 2. Number Guessing Game
Computer picks a number, user guesses with hints.
**Skills**: Random effect, loops, conditionals
```lux
fn game(): Unit with {Console, Random} = {
let secret = Random.int(1, 100)
Console.print("I'm thinking of a number 1-100...")
guessLoop(secret, 1)
}
```
### 3. Word Counter
Count words, lines, and characters in a file.
**Skills**: File effect, string operations
```lux
fn countFile(path: String): Unit with {Console, File} = {
let content = File.read(path)
let lines = String.lines(content)
let words = countWords(content)
let chars = String.length(content)
// ... display results
}
```
### 4. Simple Quiz
Multiple choice questions with scoring.
**Skills**: ADTs, pattern matching, state
```lux
type Question = { text: String, options: List<String>, correct: Int }
fn askQuestion(q: Question): Bool with {Console} = {
Console.print(q.text)
// ... display options and check answer
}
```
---
## Intermediate Projects
### 5. Contact Book
CRUD operations with file persistence.
**Skills**: File I/O, JSON, ADTs, effects
```lux
type Contact = { name: String, email: String, phone: String }
effect ContactStore {
fn add(contact: Contact): Int
fn find(name: String): Option<Contact>
fn list(): List<Contact>
fn delete(id: Int): Bool
}
// Implement handlers for file-based and in-memory storage
```
### 6. Markdown Parser
Parse basic Markdown to HTML.
**Skills**: Parsing, string manipulation, ADTs
```lux
type MarkdownNode =
| Heading(Int, String) // level, text
| Paragraph(String)
| Bold(String)
| Italic(String)
| Code(String)
| List(List<String>)
| Link(String, String) // text, url
fn parseMarkdown(input: String): List<MarkdownNode> = ...
fn toHtml(nodes: List<MarkdownNode>): String = ...
```
### 7. Simple HTTP API Client
Fetch and display data from a REST API.
**Skills**: HTTP effect, JSON parsing
```lux
fn fetchWeather(city: String): Unit with {Console, Http} = {
let response = Http.get("https://api.weather.com/city/" + city)
let data = Json.parse(response)
let temp = Json.getFloat(data, "temperature")
Console.print(city + ": " + toString(temp) + "°C")
}
```
### 8. File Backup Tool
Copy files with logging and error handling.
**Skills**: File effect, error handling, recursion
```lux
fn backup(source: String, dest: String): Unit with {File, Console, Fail} = {
if File.isDirectory(source) then
backupDirectory(source, dest)
else
backupFile(source, dest)
}
```
---
## Advanced Projects
### 9. Effect-Based Test Framework
Use effects for test isolation and assertions.
**Skills**: Custom effects, handlers, composition
```lux
effect Assert {
fn equal<T>(actual: T, expected: T): Unit
fn true(condition: Bool): Unit
fn fail(message: String): Unit
}
effect Test {
fn describe(name: String, tests: fn(): Unit): Unit
fn it(name: String, test: fn(): Unit): Unit
}
// Handlers collect results, run tests, report
```
### 10. Configuration DSL
Type-safe configuration with validation.
**Skills**: Effects, validation, ADTs
```lux
effect Config {
fn required(key: String): String
fn optional(key: String, default: String): String
fn validate(key: String, validator: fn(String): Bool): String
}
fn loadAppConfig(): AppConfig with {Config, Fail} = {
AppConfig {
host: Config.required("HOST"),
port: Config.validate("PORT", isValidPort),
debug: Config.optional("DEBUG", "false") == "true"
}
}
```
### 11. Mini Language Interpreter
Build an interpreter for a simple language.
**Skills**: Parsing, ADTs, recursion, effects
```lux
type Expr = ...
type Stmt = ...
type Value = ...
effect Runtime {
fn getVar(name: String): Value
fn setVar(name: String, value: Value): Unit
fn print(value: Value): Unit
}
fn interpret(program: List<Stmt>): Unit with {Runtime} = ...
```
### 12. Task Scheduler
Schedule and run tasks with dependencies.
**Skills**: Graphs, effects, async simulation
```lux
type Task = { id: String, deps: List<String>, action: fn(): Unit }
effect Scheduler {
fn schedule(task: Task): Unit
fn run(): Unit
fn wait(taskId: String): Unit
}
```
---
## Effect Showcase Projects
These projects specifically highlight Lux's effect system.
### 13. Transactional Operations
Rollback on failure using effects.
```lux
effect Transaction {
fn begin(): Unit
fn commit(): Unit
fn rollback(): Unit
}
fn transfer(from: Account, to: Account, amount: Int): Unit
with {Transaction, Database, Fail} = {
Transaction.begin()
Database.debit(from, amount)
Database.credit(to, amount) // If this fails, rollback
Transaction.commit()
}
```
### 14. Mock HTTP for Testing
Swap real HTTP with recorded responses.
```lux
handler recordedHttp(responses: Map<String, String>): Http {
fn get(url) = {
match Map.get(responses, url) {
Some(body) => resume(body),
None => Fail.fail("No recorded response for: " + url)
}
}
}
// Test with recorded responses
let testResponses = Map.from([
("https://api.example.com/users", "[{\"id\": 1}]")
])
run fetchUsers() with { Http = recordedHttp(testResponses) }
```
### 15. Capability-Based Security
Use effects as capabilities.
```lux
effect FileRead { fn read(path: String): String }
effect FileWrite { fn write(path: String, content: String): Unit }
effect Network { fn fetch(url: String): String }
// This function can ONLY read files - it cannot write or use network
fn processConfig(path: String): Config with {FileRead} = ...
// This function has network but no file access
fn fetchData(url: String): Data with {Network} = ...
```
### 16. Async Simulation
Model async operations with effects.
```lux
effect Async {
fn spawn(task: fn(): T): Future<T>
fn await(future: Future<T>): T
fn sleep(ms: Int): Unit
}
fn parallel(): List<Int> with {Async} = {
let f1 = Async.spawn(fn(): Int => compute1())
let f2 = Async.spawn(fn(): Int => compute2())
let f3 = Async.spawn(fn(): Int => compute3())
[Async.await(f1), Async.await(f2), Async.await(f3)]
}
```
---
## Project Complexity Guide
| Project | Effects Used | Lines of Code | Time |
|---------|--------------|---------------|------|
| Temperature Converter | Console | ~50 | 1 hour |
| Guessing Game | Console, Random | ~80 | 2 hours |
| Word Counter | Console, File | ~60 | 1 hour |
| Contact Book | Console, File, Custom | ~200 | 4 hours |
| Markdown Parser | Pure + Console | ~300 | 6 hours |
| Test Framework | Custom effects | ~400 | 8 hours |
| Mini Interpreter | Custom effects | ~600 | 16 hours |
---
## Getting Started
1. **Pick a project** at your skill level
2. **Break it down** into smaller tasks
3. **Start with types** - define your data structures
4. **Add effects** - what I/O do you need?
5. **Implement logic** - write pure functions first
6. **Test with handlers** - swap in mock handlers
## Need Help?
- Check `examples/` for working code
- Read the [Effects Guide](../guide/05-effects.md)
- Experiment in the REPL
Happy building!