# Chapter 3: Functions

Functions are the building blocks of Lux programs. They're first-class values—you can pass them around, return them, and store them in data structures.

## Defining Functions

Basic syntax:

```lux
fn name(param1: Type1, param2: Type2): ReturnType = body
```

Examples:

```lux
fn add(a: Int, b: Int): Int = a + b

fn greet(name: String): String = "Hello, " + name

fn isEven(n: Int): Bool = n % 2 == 0
```

## Single Expression vs Block Body

Simple functions use `=`:

```lux
fn square(x: Int): Int = x * x
```

Complex functions use `= { ... }`:

```lux
fn classify(n: Int): String = {
    let abs_n = if n < 0 then -n else n
    if abs_n == 0 then "zero"
    else if abs_n < 10 then "small"
    else "large"
}
```

The last expression in a block is the return value. No `return` keyword needed.

## Type Inference

Return types can often be inferred:

```lux
fn add(a: Int, b: Int) = a + b  // Returns Int
fn not(b: Bool) = !b            // Returns Bool
```

But parameter types are always required:

```lux
fn double(x) = x * 2  // Error: parameter type required
```

## Anonymous Functions (Lambdas)

Functions without names:

```lux
fn(x: Int): Int => x * 2
```

Used with higher-order functions:

```lux
List.map([1, 2, 3], fn(x: Int): Int => x * 2)  // [2, 4, 6]

List.filter([1, 2, 3, 4], fn(x: Int): Bool => x > 2)  // [3, 4]
```

Store in variables:

```lux
let double = fn(x: Int): Int => x * 2
double(5)  // 10
```

## Higher-Order Functions

Functions that take or return functions:

```lux
// Takes a function
fn apply(f: fn(Int): Int, x: Int): Int = f(x)

apply(fn(x: Int): Int => x + 1, 5)  // 6

// Returns a function
fn makeAdder(n: Int): fn(Int): Int =
    fn(x: Int): Int => x + n

let add10 = makeAdder(10)
add10(5)   // 15
add10(20)  // 30
```

## Closures

Functions capture their environment:

```lux
fn counter(): fn(): Int with {State} = {
    let count = 0
    fn(): Int with {State} => {
        State.put(State.get() + 1)
        State.get()
    }
}

// The returned function remembers `count`
```

More practical example:

```lux
fn makeMultiplier(factor: Int): fn(Int): Int =
    fn(x: Int): Int => x * factor

let triple = makeMultiplier(3)
triple(4)  // 12
triple(7)  // 21
```

## Recursion

Functions can call themselves:

```lux
fn factorial(n: Int): Int =
    if n <= 1 then 1
    else n * factorial(n - 1)

factorial(5)  // 120
```

## Tail Call Optimization

Lux optimizes tail-recursive functions:

```lux
// Not tail-recursive (stack grows)
fn factorial(n: Int): Int =
    if n <= 1 then 1
    else n * factorial(n - 1)  // Must multiply AFTER recursive call

// Tail-recursive (constant stack)
fn factorialTail(n: Int, acc: Int): Int =
    if n <= 1 then acc
    else factorialTail(n - 1, n * acc)  // Recursive call is LAST operation

fn factorial(n: Int): Int = factorialTail(n, 1)
```

The tail-recursive version won't overflow the stack.

## Function Composition

Combine functions:

```lux
fn compose<A, B, C>(f: fn(B): C, g: fn(A): B): fn(A): C =
    fn(x: A): C => f(g(x))

fn addOne(x: Int): Int = x + 1
fn double(x: Int): Int = x * 2

let addOneThenDouble = compose(double, addOne)
addOneThenDouble(5)  // 12 = (5 + 1) * 2
```

## Partial Application

Create new functions by fixing some arguments:

```lux
fn add(a: Int, b: Int): Int = a + b

// Manually partial apply
fn add5(b: Int): Int = add(5, b)

add5(3)  // 8
```

## Pipeline Style

Chain operations readably:

```lux
// Without pipeline
toString(List.sum(List.map(List.filter([1,2,3,4,5], isEven), square)))

// With intermediate variables
let nums = [1, 2, 3, 4, 5]
let evens = List.filter(nums, isEven)
let squared = List.map(evens, square)
let total = List.sum(squared)
toString(total)
```

## Functions with Effects

Functions that perform effects declare them:

```lux
fn pureAdd(a: Int, b: Int): Int = a + b  // No effects

fn printAdd(a: Int, b: Int): Unit with {Console} = {
    let sum = a + b
    Console.print("Sum: " + toString(sum))
}
```

Effects propagate:

```lux
fn helper(): Int with {Console} = {
    Console.print("Computing...")
    42
}

// Must also declare Console since it calls helper
fn main(): Int with {Console} = {
    let x = helper()
    x * 2
}
```

## Generic Functions

Functions that work with any type:

```lux
fn identity<T>(x: T): T = x

identity(42)       // 42
identity("hello")  // "hello"
identity(true)     // true

fn pair<A, B>(a: A, b: B): (A, B) = (a, b)

pair(1, "one")  // (1, "one")
```

## Summary

```lux
// Basic function
fn name(param: Type): Return = body

// Lambda
fn(x: Int): Int => x * 2

// Higher-order (takes function)
fn apply(f: fn(Int): Int, x: Int): Int = f(x)

// Higher-order (returns function)
fn makeAdder(n: Int): fn(Int): Int = fn(x: Int): Int => x + n

// Generic
fn identity<T>(x: T): T = x

// With effects
fn greet(name: String): Unit with {Console} = Console.print("Hi " + name)
```

## Next

[Chapter 4: Data Types](04-data-types.md) - Algebraic data types and pattern matching.
