# Trampoline


Trampolined interpreter using `builtins.genericClosure` for O(1) stack depth.

## `handle`

_handle: trampolined handler combinator with a custom `return` clause; interprets the computation through `handlers` from initial `state` then folds the pair via `return`._

```
handle : { return ? Identity, handlers, state ? null } -> Computation a -> { value, state }
```

Trampolined handler combinator with `return` clause.

Follows Kiselyov & Ishii's `handle_relay` pattern but trampolined
via `genericClosure` for O(1) stack depth.

**Arguments** (attrset):
- `return` — `value -> state -> { value, state }`. How to transform the final Pure value. Default: identity.
- `handlers` — `{ effectName = { param, state }: { resume | abort, state }; }`. Each must return `{ resume; state; }` or `{ abort; state; }`.
- `state` — initial handler state. Default: null.

**Handler state and closure-valued fields** — the trampoline
`deepSeq`-forces handler state at each step. Derivations and other
values that hang under `deepSeq` must be wrapped with
`fx.state.thunk.mkThunk` before being stored in handler state, and
unwrapped with `fx.state.thunk.forceThunk` after `handle` returns.
Closure-valued fields are opaque to `deepSeq` and don't need wrapping,
but any attrset field reachable from state that contains a derivation
or pointer-cyclic value does.

## `rotate`

_rotate: selectively handle known effects and rotate unknown ones outward; matches the Kyo-style handler-rotation law for nested scopes._

```
rotate : { return ? Identity, handlers, state ? null } -> Computation a -> Computation b
```

Selectively handle known effects and rotate unknown effects outward.

If the current effect has a matching handler, the handler is applied.
If it does not match, the effect is re-suspended and its continuation
is wrapped so handling resumes after that effect is interpreted by an
outer handler.

This corresponds to the Kyo-style handler rotation law
from https://gist.github.com/vic/3a7f52974a28675dbaf40b34bec74787:

```
handle(tag1, suspend(tag2, i, k), f) = suspend(tag2, i, x => handle(tag1, k(x), f))` for `tag1 != tag2
```

## `run`

_run: drive a computation through the `genericClosure` trampoline with a handler attrset and initial state; returns `{ value, state }` at O(1) stack depth._

```
run : Computation a -> Handlers -> State -> { value : a, state : State }
```

Run a computation through the `genericClosure` trampoline.

**Arguments:**
- `comp` — the freer monad computation to interpret
- `handlers` — `{ effectName = { param, state }: { resume | abort, state }; ... }`
- `initialState` — starting state passed to handlers

Handlers must return one of:

```
{ resume = value; state = newState; }  -- invoke continuation with value
{ abort  = value; state = newState; }  -- discard continuation, halt
```

This is the defunctionalized encoding of Plotkin & Pretnar (2009):
`resume` ≡ invoke continuation k(v), `abort` ≡ discard k.

Stack depth: O(1) — constant regardless of computation length.
Time: O(n) where n = number of effects in the computation.

**Handler state and closure-valued fields** — the trampoline
`deepSeq`-forces handler state at each step. Derivations and other
values that hang under `deepSeq` must be wrapped with
`fx.state.thunk.mkThunk` before being stored in handler state, and
unwrapped with `fx.state.thunk.forceThunk` after `run` returns.
Closure-valued fields are opaque to `deepSeq` and don't need wrapping,
but any attrset field reachable from state that contains a derivation
or pointer-cyclic value does.

