diff options
| author | polwex <polwex@sortug.com> | 2025-10-20 13:13:39 +0700 |
|---|---|---|
| committer | polwex <polwex@sortug.com> | 2025-10-20 13:13:39 +0700 |
| commit | d21900836f89b2bf9cd55ff1708a4619c8b89656 (patch) | |
| tree | bb3a5842ae408ffa465814c6bbf27a5002866252 /ocaml/POKES_EXPLAINED.md | |
neoinityes
Diffstat (limited to 'ocaml/POKES_EXPLAINED.md')
| -rw-r--r-- | ocaml/POKES_EXPLAINED.md | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/ocaml/POKES_EXPLAINED.md b/ocaml/POKES_EXPLAINED.md new file mode 100644 index 0000000..f0eb571 --- /dev/null +++ b/ocaml/POKES_EXPLAINED.md @@ -0,0 +1,256 @@ +# What is a "Poke"? Understanding Urbit Events + +## The Big Picture + +**Poke** is Urbit's term for "send an event to Arvo (the kernel)". + +Think of it like this: +- **Unix**: You write to a file descriptor or call `ioctl()` +- **Windows**: You send a message to a window handle +- **Urbit**: You **poke** Arvo with an **ovum** (event) + +## Event Structure: Ovum + +An `ovum` (plural: ova) is a single event. It's a simple pair: + +``` +ovum = [wire card] +``` + +Where: +- **wire**: A path identifying WHERE this event came from/goes to + - Example: `/d/term/1` (Dill terminal 1) + - Example: `/g/http/0v1a2b3c` (Eyre HTTP request) + - Example: `/a/peer/~zod` (Ames network to ~zod) + +- **card**: The actual action/data, structured as `[tag payload]` + - Example: `[%belt %ret]` (keyboard Return key) + - Example: `[%born ~]` (HTTP server started) + - Example: `[%send ...]` (Network packet) + +## The Poke Cycle + +``` +┌─────────────┐ +│ Runtime │ (Vere, Neovere, Sword) +└──────┬──────┘ + │ + │ 1. Poke with ovum [wire card] + │ + ▼ +┌─────────────┐ +│ Arvo │ (The kernel) +└──────┬──────┘ + │ + │ 2. Returns [effects new_kernel] + │ + ▼ +┌─────────────┐ +│ Runtime │ +└──────┬──────┘ + │ + │ 3. Execute effects + │ (print to terminal, send network packet, etc.) + │ + └─────► Real world I/O +``` + +## How Poke Actually Works + +From the Vere C code (and our OCaml port): + +```ocaml +(* Get poke function from kernel *) +let poke_arm = slot 23 kernel in + +(* Create gate (function) *) +let gate = nock kernel poke_arm in + +(* Call gate with event *) +let result = slam gate event in + +(* Result is [effects new_kernel] *) +match result with +| Cell (effects, new_kernel) -> + (* Update kernel state *) + kernel := new_kernel; + (* Execute effects *) + execute_effects effects +``` + +## Types of Events (Common Cards) + +### Terminal (Dill) +``` +Wire: /d/term/1 + +Inputs (to Arvo): + [%belt %ret] - Return key + [%belt %bac] - Backspace + [%belt [%txt "hello"]] - Text input + +Outputs (from Arvo): + [%blit %lin "text"] - Print line + [%blit %clr] - Clear screen + [%blit %hop 5] - Move cursor +``` + +### HTTP (Eyre) +``` +Wire: /g/http/0v... + +Inputs: + [%request request-data] - HTTP request + +Outputs: + [%http-response response] - HTTP response +``` + +### Network (Ames) +``` +Wire: /a/peer/~zod + +Inputs: + [%hear packet] - Received packet + +Outputs: + [%send packet] - Send packet +``` + +### Timer (Behn) +``` +Wire: /b/wait/... + +Inputs: + [%wake ~] - Timer fired + +Outputs: + [%doze time] - Set next timer +``` + +## Boot vs Runtime + +### During Boot (Lifecycle) +```ocaml +(* Boot uses a special formula, not poke! *) +let lifecycle = [2 [0 3] [0 2]] in +let kernel = nock event_list lifecycle in +(* No effects during boot - just builds kernel *) +``` + +Boot events are **batch processed** via the lifecycle formula. +They build up the kernel state but don't produce effects. + +### After Boot (Runtime) +```ocaml +(* Runtime uses poke for each event *) +let effects = poke kernel event in +(* Effects need to be executed! *) +``` + +Runtime events are **individually poked** and produce effects that must be executed. + +## Why Two Different Mechanisms? + +**Lifecycle** (boot): +- Process many events as a batch +- Pure state building +- No I/O needed +- Fast initialization + +**Poke** (runtime): +- Process one event at a time +- Produces side effects +- Needs I/O execution +- Interactive operation + +## Our Implementation + +### State.poke (lib/state.ml:66) +```ocaml +let poke state event = + (* Get poke gate from kernel slot 23 *) + let formula = slot poke_formula_axis kernel in + let gate = nock_on kernel formula in + + (* Slam gate with event *) + let result = slam_on gate event in + + (* Extract effects and new kernel *) + match result with + | Cell (effects, new_core) -> + state.roc <- new_core; (* Update kernel *) + state.eve <- succ eve; (* Increment event number *) + effects (* Return effects *) +``` + +### What We Need To Do Next + +1. ✅ **Poke works** - ~11,000 pokes/second +2. ✅ **Effects returned** - Structure is correct +3. ⏳ **Parse effects** - Extract wire/card from effect list +4. ⏳ **Route effects** - Send to appropriate driver (Dill, Eyre, Ames) +5. ⏳ **Execute effects** - Actually do the I/O +6. ⏳ **Event loop** - Continuous input → poke → effects → output + +## Example: Terminal Input Flow + +``` +1. User types "hello" and hits Enter + +2. Terminal driver creates belt event: + ovum = [/d/term/1 [%belt [%txt "hello"]]] + [/d/term/1 [%belt %ret]] + +3. Poke Arvo: + effects = State.poke state ovum + +4. Arvo processes input, maybe runs dojo command + +5. Arvo returns effects: + [ + [/d/term/1 [%blit [%lin "result here"]]] + [/d/term/1 [%blit [%lin "~zod:dojo> "]]] + ] + +6. Dill driver executes effects: + - Print "result here" to terminal + - Print prompt "~zod:dojo> " + +7. Wait for next input +``` + +## Performance Notes + +From our benchmarks: +- **Poke speed**: ~11,000 pokes/second (0.09ms each) +- **Boot time**: ~1s for full solid pill +- **Memory**: No limit (OCaml GC manages it) + +This is plenty fast for interactive use! + +## Key Insight + +**Poke is just a function call into the kernel.** + +There's no magic. It's: +1. Load kernel from memory +2. Call its poke function (slot 23) +3. Get back effects + new kernel +4. Replace old kernel with new kernel +5. Execute the effects + +The genius is that the **entire OS state** is in that kernel noun, and poke is a pure function that transforms it. + +## References + +- `vere/pkg/vere/mars.c` - C implementation +- `vere/pkg/noun/vortex.c:u3v_poke()` - Poke implementation +- `docs/runtime/api.md` - API documentation (check `/docs/runtime/`) +- Arvo source: `sys/arvo.hoon` in urbit/urbit repo + +## See Also + +- `BOOT_COMPARISON.md` - Why lifecycle ≠ poke +- `EFFECTS.md` - Effects processing architecture +- `LOOM.md` - Memory management (or lack thereof!) |
