diff options
Diffstat (limited to 'js/baba-yaga/scratch/docs/IO.md')
-rw-r--r-- | js/baba-yaga/scratch/docs/IO.md | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/js/baba-yaga/scratch/docs/IO.md b/js/baba-yaga/scratch/docs/IO.md new file mode 100644 index 0000000..6399b66 --- /dev/null +++ b/js/baba-yaga/scratch/docs/IO.md @@ -0,0 +1,198 @@ +# IO Plan: Events and Effects for Baba Yaga + +This document proposes an opinionated IO interface for embedding Baba Yaga programs into host systems. We introduce two core primitives for evented IO: + +- `io.listen` — subscribe to external events by name +- `io.emit` — publish events (commands/effects) produced by Baba Yaga + +The design follows a TEA/FRP-inspired architecture: a single unidirectional data-flow where external inputs become events, user logic transforms state and produces new effects, and the host executes those effects and feeds results/errors back as new events. + +## Goals + +- One clean, documented integration path for host systems (Node, browser, services) +- Pure, testable user code: all side-effects mediated by the host via `io.emit` and `io.listen` +- Deterministic core: program logic remains a function of prior state and incoming events +- Easy to build SDKs/harnesses around a small surface area + +## Mental Model + +``` +External world -> Events -> Baba Yaga Program -> Effects -> Host executes -> more Events +``` + +- Events are immutable data records identified by a string `topic` and a payload `data`. +- Effects are commands (also a `topic` + `data`) that the host is responsible for executing. +- Round-trips (request/response) are modeled as a pair of topics, e.g. `http.request` and `http.response`. + +## Core API (Language-Level Semantics) + +The following describes the intended semantics. Implementation comes later. + +- `io.listen topic handler` — registers a handler function for events matching `topic`. + - `topic : String` + - `handler : (event: { topic: String, data: Table }) -> Unit` + - Returns `Unit`. + - Handlers are pure (no direct side-effects); they may call `io.emit` to request effects. + +- `io.emit topic data` — emits an effect (command) to the host. + - `topic : String` + - `data : Table | List | String | Int | Float | Bool` + - Returns `Unit`. + +### Event Routing and Namespacing + +- Topics use dotted names, e.g. `app.tick`, `db.query`, `ws.message`, `http.request`. +- Conventionally, effects (commands) and events are separate namespaces; e.g. commands under `cmd.*` and events under `evt.*`, or paired `http.request`/`http.response`. + +## Program Structure (TEA-flavored) + +We encourage a TEA-like structure: + +```baba +// State is a Table (author’s choice) +State : type alias Table; // conceptual + +// Update: State x Event -> (State, Effects) +update : (state, event) -> { state: State, effects: List } -> + when event.topic is + "app.init" then { state: { count: 0 }, effects: [] } + "app.tick" then { state: { count: state.count + 1 }, effects: [] } + "http.response" then { state: state, effects: [] } + _ then { state: state, effects: [] }; + +// Subscriptions: declare external event interest +init : -> + io.listen "app.tick" (e -> io.emit "cmd.render" { count: 0 }); + +// Effect producers inside handlers +handleTick : e -> io.emit "cmd.render" { count: e.data.count }; +``` + +Notes: +- `io.listen` is declarative; the host wires it to real sources. +- Handlers do not perform side-effects directly but may `io.emit` effects. + +## Host SDK / Harness + +The host must implement a small, stable interface to embed and drive programs. + +Suggested host-side API (pseudo-TypeScript): + +```ts +type Event = { topic: string; data: unknown }; +type Effect = { topic: string; data: unknown }; + +interface BabaProgram { + // Run a program to completion on an input source; returns a controller + start(initialEvents?: Event[]): ProgramController; +} + +interface ProgramController { + // Push an external event into the program + dispatch(event: Event): void; + // Subscribe to effects emitted by the program + onEffect(subscriber: (effect: Effect) => void): () => void; // unsubscribe + // Stop the program and cleanup + stop(): void; +} + +interface HostIO { + // Wire program-level io.listen registrations to host event sources + addListener(topic: string, handler: (event: Event) => void): () => void; // unsubscribe + // Deliver effects to the host for execution + deliver(effect: Effect): void; +} +``` + +### Responsibilities + +- Program side: + - Calls `io.listen` to declare interests (topics and handlers). + - Calls `io.emit` to request side-effects. + +- Host side: + - Maps external sources (timers, websockets, HTTP, DB) to events and pushes them into the program via `dispatch`. + - Executes effects received from `onEffect` subscribers; potentially pushes result events back (e.g., `http.response`). + - Manages lifecycle (start/stop) and isolation (per session or per process). + +### Startup sequence and initial effects + +- The host should invoke `program.start([{ topic: 'app.init', data: {...} }])` to bootstrap. +- User code can `io.listen "app.init"` to initialize state and immediately `io.emit` effects. +- This supports emitting to external systems as the very first action (e.g., schedule work, send a greeting, request data). + +Example (Baba Yaga): + +```baba +onInit : e -> + io.emit "cmd.fetch" { url: "https://api.example.com/data" }; + +main : -> + io.listen "app.init" onInit; +``` + +### Reference Topics + +We recommend a small standard vocabulary: + +- `app.init` — sent once at startup +- `app.tick` — periodic timer events +- `http.request` / `http.response` +- `ws.message` / `ws.send` +- `db.query` / `db.result` + +These are guidelines; projects can add more namespaced topics. + +## Example Embedding Flow (Host Pseudocode) + +```ts +const controller = program.start([{ topic: 'app.init', data: {} }]); + +controller.onEffect(async (eff) => { + if (eff.topic === 'http.request') { + const res = await fetch(eff.data.url, { method: eff.data.method }); + controller.dispatch({ topic: 'http.response', data: { status: res.status } }); + } + if (eff.topic === 'cmd.render') { + render(eff.data); // user-defined + } +}); + +// External source -> event +ws.onmessage = (msg) => controller.dispatch({ topic: 'ws.message', data: msg }); +``` + +## Testing Strategy + +- Treat user logic as pure functions of `(state, event) -> { state, effects }`. +- Unit-test handlers and updaters by asserting emitted effects and next state. +- Integration-test host harness by simulating effect execution and event feedback. + +## Summary + +- `io.listen` and `io.emit` define a minimal, opinionated bridge between Baba Yaga and the outside world. +- A TEA/FRP-inspired loop ensures purity and testability. +- A small host SDK surface (dispatch/onEffect) provides a unified way to embed programs across environments. + +## Application profiles (guidance) + +- Near real-time data processing + - Sources: streams, queues, file watchers -> `evt.ingest.*` + - Effects: enrichment, writes, notifications -> `cmd.write.*`, `cmd.notify.*` + - Backpressure: batch events or throttle at host; program remains pure + +- Games / simulations + - Drive time with `app.tick` at a fixed cadence + - Program updates state and emits `cmd.render` or `cmd.sound` effects; host renders + +- WebSocket comms + - Events: `ws.message` with parsed payload + - Effects: `ws.send` with outbound frames; host maps to actual socket + +- Data transformation/search + - Input events: `evt.query` or `evt.batch` + - Effects: `cmd.response` with transformed payloads + +This single event/effect interface scales across these use cases without leaking side-effects into program logic. + + |