# 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(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(x: T): T = x identity(42) // 42 identity("hello") // "hello" identity(true) // true fn pair(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(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.