Files
lux/docs/guide/01-introduction.md
Brandon Lucas 44f88afcf8 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>
2026-02-13 17:43:41 -05:00

4.3 KiB

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)

git clone https://github.com/your-org/lux
cd lux
cargo build --release

# Add to PATH
export PATH="$PATH:$(pwd)/target/release"

With Nix

# Enter development shell
nix develop

# Or build
nix build
./result/bin/lux

Verify Installation

$ 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:

// hello.lux - Your first Lux program

fn main(): Unit with {Console} =
    Console.print("Hello, Lux!")

let output = run main() with {}

Run it:

$ lux hello.lux
Hello, Lux!

Let's break this down:

The Function Signature

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

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

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:

$ 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:

// 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 {}
$ 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:

// 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:

Or jump ahead to what makes Lux special: