# 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.
