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>
223 lines
4.3 KiB
Markdown
223 lines
4.3 KiB
Markdown
# Chapter 1: Introduction to Lux
|
|
|
|
Welcome to Lux, a functional programming language where side effects are first-class citizens.
|
|
|
|
## Why Lux?
|
|
|
|
Every program does more than compute. It reads files, makes network requests, prints output, handles errors. In most languages, these *effects* are invisible—a function's type doesn't tell you what it might do.
|
|
|
|
Lux changes this. Effects are:
|
|
- **Visible** in the type signature
|
|
- **Controllable** via handlers
|
|
- **Composable** without boilerplate
|
|
|
|
This isn't just academic. It means:
|
|
- Tests can swap real I/O for mocks—automatically
|
|
- Error handling is explicit, not exceptional
|
|
- Dependencies are injected through the type system
|
|
|
|
## Installation
|
|
|
|
### From Source (Cargo)
|
|
|
|
```bash
|
|
git clone https://github.com/your-org/lux
|
|
cd lux
|
|
cargo build --release
|
|
|
|
# Add to PATH
|
|
export PATH="$PATH:$(pwd)/target/release"
|
|
```
|
|
|
|
### With Nix
|
|
|
|
```bash
|
|
# Enter development shell
|
|
nix develop
|
|
|
|
# Or build
|
|
nix build
|
|
./result/bin/lux
|
|
```
|
|
|
|
### Verify Installation
|
|
|
|
```bash
|
|
$ lux --version
|
|
Lux 0.1.0
|
|
|
|
$ lux --help
|
|
Usage: lux [OPTIONS] [FILE]
|
|
|
|
Options:
|
|
-c, --check Type check without running
|
|
--lsp Start LSP server
|
|
--repl Start interactive REPL
|
|
-h, --help Print help
|
|
```
|
|
|
|
## Your First Program
|
|
|
|
Create a file called `hello.lux`:
|
|
|
|
```lux
|
|
// hello.lux - Your first Lux program
|
|
|
|
fn main(): Unit with {Console} =
|
|
Console.print("Hello, Lux!")
|
|
|
|
let output = run main() with {}
|
|
```
|
|
|
|
Run it:
|
|
|
|
```bash
|
|
$ lux hello.lux
|
|
Hello, Lux!
|
|
```
|
|
|
|
Let's break this down:
|
|
|
|
### The Function Signature
|
|
|
|
```lux
|
|
fn main(): Unit with {Console}
|
|
```
|
|
|
|
- `fn main()` - A function named `main` with no parameters
|
|
- `: Unit` - Returns `Unit` (like `void`)
|
|
- `with {Console}` - **Uses the Console effect**
|
|
|
|
That last part is key. The type tells us this function prints to the console. A function without `with {...}` is *pure*—it can only compute.
|
|
|
|
### The Body
|
|
|
|
```lux
|
|
Console.print("Hello, Lux!")
|
|
```
|
|
|
|
`Console.print` is an *effect operation*. It's not a regular function—it's a request to the environment to print something.
|
|
|
|
### Running Effects
|
|
|
|
```lux
|
|
let output = run main() with {}
|
|
```
|
|
|
|
The `run ... with {}` expression executes a computation with its effects. The `{}` means "use the default handlers for all effects." Console's default handler prints to stdout.
|
|
|
|
## The REPL
|
|
|
|
Lux has an interactive mode:
|
|
|
|
```bash
|
|
$ lux
|
|
Lux v0.1.0 - Type :help for commands
|
|
|
|
>
|
|
```
|
|
|
|
Try some expressions:
|
|
|
|
```
|
|
> 1 + 2
|
|
3
|
|
|
|
> "Hello, " + "World"
|
|
"Hello, World"
|
|
|
|
> [1, 2, 3]
|
|
[1, 2, 3]
|
|
|
|
> List.map([1, 2, 3], fn(x: Int): Int => x * 2)
|
|
[2, 4, 6]
|
|
```
|
|
|
|
Define functions:
|
|
|
|
```
|
|
> fn square(x: Int): Int = x * x
|
|
<function>
|
|
|
|
> square(5)
|
|
25
|
|
|
|
> fn greet(name: String): Unit with {Console} = Console.print("Hi, " + name)
|
|
<function>
|
|
|
|
> run greet("Alice") with {}
|
|
Hi, Alice
|
|
```
|
|
|
|
REPL commands:
|
|
|
|
```
|
|
:help - Show help
|
|
:type e - Show type of expression
|
|
:quit - Exit REPL
|
|
:clear - Clear screen
|
|
```
|
|
|
|
## A Slightly Bigger Program
|
|
|
|
Let's write a program that asks for your name:
|
|
|
|
```lux
|
|
// greet.lux
|
|
|
|
fn askName(): String with {Console} = {
|
|
Console.print("What's your name?")
|
|
Console.readLine()
|
|
}
|
|
|
|
fn main(): Unit with {Console} = {
|
|
let name = askName()
|
|
Console.print("Hello, " + name + "!")
|
|
}
|
|
|
|
let output = run main() with {}
|
|
```
|
|
|
|
```bash
|
|
$ lux greet.lux
|
|
What's your name?
|
|
> Alice
|
|
Hello, Alice!
|
|
```
|
|
|
|
Notice how:
|
|
- `askName` returns `String` but has `with {Console}` because it does I/O
|
|
- Both functions declare their effects
|
|
- The `run` at the end provides the runtime
|
|
|
|
## Pure vs Effectful
|
|
|
|
Here's the key insight. Compare:
|
|
|
|
```lux
|
|
// Pure - no effects, only computes
|
|
fn add(a: Int, b: Int): Int = a + b
|
|
|
|
// Effectful - uses Console
|
|
fn printSum(a: Int, b: Int): Unit with {Console} =
|
|
Console.print(toString(a + b))
|
|
```
|
|
|
|
You can call `add` from anywhere. But `printSum` can only be called from:
|
|
1. Another function that declares `Console`
|
|
2. Inside a `run ... with {}` block
|
|
|
|
This is the effect discipline. Effects propagate up until handled.
|
|
|
|
## What's Next
|
|
|
|
Now that you can run programs, let's learn:
|
|
|
|
- [Chapter 2: Basic Types](02-basic-types.md) - Numbers, strings, booleans
|
|
- [Chapter 3: Functions](03-functions.md) - Definitions, closures, composition
|
|
- [Chapter 4: Data Types](04-data-types.md) - ADTs and pattern matching
|
|
|
|
Or jump ahead to what makes Lux special:
|
|
|
|
- [Chapter 5: Effects](05-effects.md) - The core innovation
|