# Effects System in Neovere ## What Are Effects? When Arvo processes an event via poke, it returns: ``` [effects new_core] ``` Where `effects` is a list of effects to execute in the outside world. ## Effect Structure Each effect is a pair: `[wire card]` - **Wire**: Path identifying the request (for response routing) - **Card**: The actual effect to execute Example effects: ``` [/d/term/1 [%blit [%lin [%& "hello"]]]] // Print "hello" to terminal [/d/term/1 [%blit [%mor ...]]] // Multiple blits [/g/http/0v123 [%http-response ...]] // HTTP response [/a/peer/~zod [%send ...]] // Network packet ``` ## Effect Types (Cards) ### Terminal (Dill) - `%blit` - Print to terminal - `%lin` - Line of styled text - `%klr` - Colored/styled text - `%mor` - Multiple blits - `%hop` - Move cursor - `%clr` - Clear screen - `%logo` - Show logo/sigil ### HTTP (Eyre) - `%http-response` - Send HTTP response - `%response` - Generic response ### Network (Ames) - `%send` - Send packet to another ship - `%turf` - DNS/networking config ### Filesystem (Clay) - `%ergo` - File change notification - `%hill` - Mount/unmount ### Timer (Behn) - `%doze` - Set timer - `%wake` - Timer fired ## Our Approach: Eio-Based Effects Processing ### Architecture ``` ┌─────────────────────────────────────────┐ │ Event Loop (Eio) │ │ - Read terminal input │ │ - Handle timers │ │ - Network I/O │ └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Poke Arvo with Event │ │ result = State.poke state event │ └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Parse Effects from Result │ │ match result with │ │ | Cell (effects, new_core) -> ... │ └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Route Effects to Drivers │ │ - Dill: terminal I/O │ │ - Eyre: HTTP server (future) │ │ - Ames: networking (future) │ └─────────────────────────────────────────┘ ``` ### Phase 1: Terminal (Dill) Driver **Goal:** Boot a ship and see output in the terminal **What we need:** 1. Parse `%blit` effects 2. Render styled text to ANSI terminal 3. Read keyboard input (belt events) 4. Poke Arvo with belt events 5. Display blit effects **Implementation:** ```ocaml (* lib/effects.ml *) module Effect = struct type blit = | Lin of string (* Simple line *) | Klr of styled_text (* Colored text *) | Mor of blit list (* Multiple *) | Hop of int (* Cursor move *) | Clr (* Clear screen *) type card = | Blit of blit | Logo | Unknown of Noun.noun type t = { wire: Noun.noun; card: card; } let parse_effect noun = ... let parse_effects effects_noun = ... end (* lib/dill.ml *) module Dill = struct let render_blit = function | Effect.Lin text -> Printf.printf "%s\n%!" text | Effect.Klr styled -> (* Convert to ANSI codes *) render_styled styled | Effect.Mor blits -> List.iter render_blit blits | Effect.Hop n -> (* ANSI cursor move *) ... | Effect.Clr -> (* ANSI clear screen *) Printf.printf "\x1b[2J\x1b[H%!" let handle_effects effects = effects |> Effect.parse_effects |> List.filter (fun e -> is_dill_wire e.wire) |> List.iter (fun e -> match e.card with | Effect.Blit b -> render_blit b | _ -> ()) end ``` ### Phase 2: Event Loop with Eio ```ocaml (* bin/neovere_live.ml *) open Eio.Std let run_ship env state = let stdin = Eio.Stdenv.stdin env in let clock = Eio.Stdenv.clock env in (* Start terminal reader fiber *) Fiber.both (fun () -> (* Read keyboard input *) while true do let line = Eio.Flow.read_line stdin in let belt_event = Dill.make_belt_event line in let effects = State.poke state belt_event in Dill.handle_effects effects done) (fun () -> (* Timer fiber *) while true do Eio.Time.sleep clock 1.0; (* Handle doze/wake *) done) ``` ### Phase 3: Structured Concurrency Eio's strength is **structured concurrency** - no callback hell! ```ocaml let handle_http_request request = (* Run in fiber, naturally concurrent *) Switch.run @@ fun sw -> (* Create ovum for HTTP request *) let ovum = make_http_ovum request in (* Poke Arvo *) let effects = State.poke state ovum in (* Parse effects, find HTTP response *) match find_http_response effects with | Some response -> send_response response | None -> send_404 () let main env = (* All concurrent activities in one place *) Switch.run @@ fun sw -> Fiber.all [ terminal_input_loop env sw; timer_loop env sw; http_server env sw; ames_network env sw; ] ``` ## Implementation Plan ### Sprint 1: Basic Effects ✓ (Next!) - [ ] Create `lib/effects.ml` - effect parsing - [ ] Parse `[effects new_core]` from poke result - [ ] Parse individual effects `[wire card]` - [ ] Parse blit cards - [ ] Test by logging parsed effects ### Sprint 2: Dill Driver - [ ] Create `lib/dill.ml` - terminal I/O - [ ] Render %blit effects to terminal - [ ] Handle styled text (ANSI codes) - [ ] Read keyboard input - [ ] Create belt events from input - [ ] Round-trip: input → poke → effects → output ### Sprint 3: Event Loop - [ ] Create `bin/neovere_live.ml` - interactive runtime - [ ] Eio-based event loop - [ ] Terminal input fiber - [ ] Effects processing fiber - [ ] Graceful shutdown ### Sprint 4: Full Terminal Experience - [ ] Handle all blit types (hop, clr, etc.) - [ ] Proper cursor control - [ ] Line editing support - [ ] History/readline integration - [ ] Status line ## Testing Strategy ### Test 1: Parse Effects ```ocaml (* After boot, send a test event and parse effects *) let test_event = make_test_event "hello" in let result = State.poke state test_event in match result with | Cell (effects, _) -> let parsed = Effect.parse_effects effects in List.iter (fun e -> Printf.printf "Effect: wire=%s card=%s\n" (show_wire e.wire) (show_card e.card) ) parsed ``` ### Test 2: Dill Roundtrip ```ocaml (* Send belt, receive blit *) let belt = Dill.make_belt_event "~zod/try/1" in let result = State.poke state belt in Dill.handle_effects result (* Should print something to terminal! *) ``` ### Test 3: Interactive Session ```bash $ dune exec bin/neovere_live.exe zod ~zod:dojo> (add 2 2) 4 ~zod:dojo> "hello world" "hello world" ~zod:dojo> ``` ## Eio Advantages for Urbit 1. **Structured Concurrency** - All fibers have clear lifetime 2. **Backpressure** - Natural flow control 3. **Cancellation** - Clean shutdown of all I/O 4. **Multicore** - Can use multiple cores for I/O 5. **Type Safety** - Eio ensures proper resource management ## Effect Wire Routing Wires tell us where effects came from: ``` /d/term/1 -> Dill (terminal 1) /g/http/... -> Eyre (HTTP) /a/peer/~zod -> Ames (network to ~zod) /c/sync/... -> Clay (filesystem) /b/wait/... -> Behn (timers) ``` We route by examining the wire structure. ## Next Steps 1. **Now:** Create basic effect parsing 2. **Next:** Dill blit rendering 3. **Then:** Keyboard input + belt events 4. **Finally:** Full event loop with Eio Let's start with `lib/effects.ml`!