summaryrefslogtreecommitdiff
path: root/ocaml/EFFECTS.md
diff options
context:
space:
mode:
Diffstat (limited to 'ocaml/EFFECTS.md')
-rw-r--r--ocaml/EFFECTS.md305
1 files changed, 305 insertions, 0 deletions
diff --git a/ocaml/EFFECTS.md b/ocaml/EFFECTS.md
new file mode 100644
index 0000000..1c5db5d
--- /dev/null
+++ b/ocaml/EFFECTS.md
@@ -0,0 +1,305 @@
+# 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`!