# Getting Started

This guide walks you through using `incr` from your first computation to advanced patterns.

The core target-API examples in this guide are mirrored by checked literate
tests in [`target_api_examples.mbt.md`](target_api_examples.mbt.md), so changes
to constructor, read, committed-change callback, or batch rollback semantics are
caught by `moon check`.

## Installation

Add `incr` to your `moon.pkg`:

```moonbit nocheck
import {
  "dowdiness/incr",
}
```

## Your First Incremental Computation

### Step 1: Create a Runtime, Inputs, and Derived Values

Start with a `Runtime` (it owns the dependency graph, revision counter, and
garbage-collection roots), create inputs — your settable values, the leaves of
the graph — and derive computations from them. Derived values compute lazily
and track their dependencies automatically.

```mbt nocheck
///|
let rt = @incr.Runtime()

///|
let price = rt.input(100, label="price")

///|
let quantity = rt.input(5, label="quantity")

///|
let total = price.derived2(quantity, (p, q) => p * q, label="total")
```

For derived values reading more than two or three cells, use a compute closure:
`@incr.Derived(rt, () => ..., label="...")`.

> **Tip:** Always set a `label`. It has no runtime cost and makes cycle error messages and debug output much easier to read. For example, instead of `"Runtime 0 / Cell 2 → Cell 0 → …"` you'll see `"price → total → …"`.

### Step 2: Group Lifetimes with a Scope (when needed)

For long-lived subsystems, create the same cells through a `Scope` so one
`scope.dispose()` tears the group down. The construction API is otherwise
identical — `scope.input(...)` / `scope.derived(...)` instead of
`rt.input(...)` / `Derived(rt, ...)`:

```mbt nocheck
///|
let rt = @incr.Runtime()

///|
let scope = @incr.Scope::new(rt)

///|
let scoped_price = scope.input(100, label="price")

///|
let scoped_total = scope.derived(() => scoped_price.get() * 2, label="total")
```

Skip scopes for short-lived or test code; add them when a group of cells
shares a lifetime. `RuntimeContext` remains available for app structs that
want to hide the runtime behind their own context type.

### Step 3: Read and Update

`Derived::get()` is only valid inside another derived compute function. From
outside the graph — top-level code, tests, event handlers — read with `read()`
or `read_or_abort()`. Both forms recompute lazily.

```mbt check
///|
test "getting started: read and update" {
  let rt = @incr.Runtime()
  let price = rt.input(100, label="price")
  let quantity = rt.input(5, label="quantity")
  let total = price.derived2(quantity, (p, q) => p * q, label="total")

  inspect(total.read_or_abort(), content="500")

  quantity.set(10)
  inspect(total.read_or_abort(), content="1000")
}
```

### Step 4: Prefer Graceful Reads

`read_or_abort()` is convenient but aborts on cycle errors. For resilient applications, use `read()`:

```mbt check
///|
test "getting started: graceful read" {
  let rt = @incr.Runtime()
  let price = rt.input(100, label="price")
  let quantity = rt.input(5, label="quantity")
  let total = price.derived2(quantity, (p, q) => p * q, label="total")

  match total.read() {
    Ok(value) => inspect(value, content="500")
    Err(err) => abort(err.format_path())
  }
}
```

`CycleError::format_path` takes no runtime — labels are captured at
detection time, so errors render the same regardless of where and when you
format them.

Inside another derived computation, use `get()` when you want to handle cycles
as values and `get_or_abort()` when an aborting strict read is acceptable.

### Step 5: Observe Committed Changes with `set_on_change`

Use `Runtime::set_on_change` to run a callback whenever the runtime commits a change.

```mbt check
///|
test "getting started: committed change callback" {
  let rt = @incr.Runtime()
  let quantity = rt.input(10, label="quantity")
  let changes : Ref[Int] = { val: 0 }

  rt.set_on_change(() => changes.val = changes.val + 1)

  quantity.set(12)
  inspect(changes.val, content="1")

  // Same-value set is a no-op, callback does not fire.
  quantity.set(12)
  inspect(changes.val, content="1")
}
```

### Step 6: Batch Rollback on Raised Errors

Batch writes are transactional for raised errors:

```mbt check
///|
suberror GettingStartedBatchStop {
  GettingStartedStop
}

///|
test "getting started: batch_result rollback" {
  let rt = @incr.Runtime()
  let amount = rt.input(100)
  let result = rt.batch_result(fn() raise {
    amount.set(999)
    raise GettingStartedStop
  })

  inspect(result is Err(_), content="true")
  inspect(amount.get(), content="100")
}
```

Note: `abort()` is not catchable in MoonBit. Rollback applies to raised errors only.

## Complete Example

```mbt check
///|
test "getting started: complete example" {
  let rt = @incr.Runtime()

  let base_price = rt.input(100, label="base_price")
  let tax_rate = rt.input(0.1, label="tax_rate")
  let quantity = rt.input(2, label="quantity")

  let subtotal = base_price.derived2(
    quantity,
    (p, q) => p * q,
    label="subtotal",
  )
  let tax = subtotal.map(v => v.to_double() * tax_rate.get(), label="tax")
  let total = subtotal.map2(tax, (s, t) => s.to_double() + t, label="total")

  inspect(subtotal.read_or_abort(), content="200")
  inspect(tax.read_or_abort(), content="20")
  inspect(total.read_or_abort(), content="220")

  quantity.set(3)
  inspect(total.read_or_abort(), content="330")
}
```

## What Makes It Incremental?

When you call `quantity.set(3)`, `incr` doesn't immediately recompute everything. Instead:

1. It notes that `quantity` changed at a new revision
2. When you read `total.read_or_abort()`, it checks if `total`'s dependencies changed
3. It walks the dependency chain: `total` → `subtotal` → `quantity` (changed!)
4. Only the affected derived values (`subtotal`, `tax`, `total`) recompute

If you had 100 other derived values that don't depend on `quantity`, they wouldn't even be checked.

## Next Steps

- [Core Concepts](./concepts.mbt.md) — Understand Inputs, Derived values, Revisions, and Durability
- [API Reference](./api-reference.mbt.md) — Complete reference for all public types and methods
- [Cookbook](./cookbook.mbt.md) — Common patterns and recipes
