Completes plan 2 Tasks 7-8. owner.ml: mutable-value-semantics flow analysis producing the four plan-3 emitter tables (moves, drops incl. LIVE-MASK for trap unwinding, rc with elision, residual borrow sites) plus WO-E301-304 two-site diagnostics. Alias questions run over canonicalized places, so a double-mut reached through let-bound aliases lands in the residual table like the direct form; dump.ml's contract notes the emitter must coalesce guards per operand. main.ml: directory discovery, cross-file programs (symbols merge before bodies check), diagnostics ordered by (file,line,col), new WO-E214 for a name declared in two files. New docs/plan/oop-vm/01-error-catalog.md (14 emitted + 10 reserved codes), un-ignored so both plan tracks can cite it; justfile regains woc-*. builtin_scalars is now the five that work: Int, Bool, Text, Timestamp, Id. Money/SKU/Float and the abstract_types allowlist are gone — `abstract` never lexed, and Float had no literal syntax and no wob kind, so no value could exist. Fixtures and samples retype Money->Int, SKU->Text. The abstract newtype feature is rejected outright (verdict row adopt->reject); haxe-parity Task 7 keeps `is`. nullable-types-implementation.md corrected: ?T is plumbed but UNENFORCED (E211-213 declared, never emitted; probe exits 0), handed to haxe-parity Task 6 as next work item. Records all 10 dead codes incl. E205 — interface satisfaction is unchecked. crates/rt keeps its Money/SKU fixtures (opaque strings, Stage 2). Gate: build warning-clean, 14 + 264 checks 0 failures, pricing golden exit 0, docs/examples histograms unchanged (13/70, zero WO-E225). |
||
|---|---|---|
| .. | ||
| types | ||
| ui/pricing | ||
| main.wo | ||
| README.md | ||
| wo.toml | ||
pricing — class model + live pricing demo
A Price class and a Product class with methods — products have prices — and a /pricing screen that shows live price data for selected products. Data stays in RAM; one product is readable by millions of customers at once; a price update pushes live to millions of subscribers; all concurrency is Linux kernel I/O (epoll today, io_uring per the scale-out plan).
Status: 13a + 13b shipped.
classdeclarations parse, the demo serves real REST CRUD, and methods execute over RPC —wo run docs/examples/pricing(orjust pricing-demo) servesPOST /api/products/:id/set_priceand:id/current_priceas row-scoped transactions: the whole body commits as one WAL frame, anassert … otherwise abortrolls everything back (409).subscribeis a 501 stub until 13c; the UI is design-only until 13d/plan 14. The master plan isdocs/plan/13-class-model-live-pricing.md.
The class model in one paragraph
class = state + methods, no inheritance. Fields, defaults, ref/multi, service, policy, on <event> — all exactly as in type — plus fn methods with an implicit self receiver that run as row-scoped transactions (in txn [snapshot], the same machinery as the ecommerce sample's free-standing fn checkout). No extends, no override, no virtual dispatch: "is-a" is a tagged union, "has-a" is a ref/multi edge. Go-style encapsulation, not Java-style hierarchies.
Layout
The UI follows MVC (docs/plan/exploration/ui/08-mvc-structure.md), with the same screen anatomy as the v1 Angular app (reference/writeonce-app/src/app/article/) collapsed into the single binary: the model is the class itself, the view is plain .htmlx with external .scss, and the controller is a .wo file that binds the model into the view and is the only place UI may call class methods.
pricing/
├── wo.toml # app manifest
├── main.wo # entry point: insert product, LIVE subscribe, set_price
├── types/ # MODEL — classes: schema + methods
│ ├── price.wo # class Price — amount/currency/at + fn discounted(pct)
│ └── product.wo # class Product — prices: multi Price + fn current_price /
│ # fn set_price + service rest /api/products
└── ui/
└── pricing/ # one screen = one MVC triplet
├── pricing.wo # CONTROLLER — route, model: bindings, actions → class methods
├── pricing.htmlx # VIEW — plain htmlx (Mustache + wo:live/wo:bind), logic-free
└── pricing.scss # VIEW styles — external SCSS, compiled at `wo build`
What runs when
| File / feature | Goes live in | Plan |
|---|---|---|
class parses; /api/products CRUD serves |
13a ✅ shipped | lexer/parser/AST + spec amendments |
set_price / current_price over RPC (POST /api/products/:id/set_price) |
13b ✅ shipped | method execution, row-scoped txn, one WAL frame per call |
@table(name: "prices", index: [product, at]) + indexed DML — history() via select Price{ product == self.id }, GET /api/prices?product=1 |
✅ shipped (13 follow-up) | engine secondary indexes, find_by, REST filters |
subscribe / LIVE select — delta on every commit, WebSocket at /api/products/live |
13c | subscription registry (scoped Stage 3) |
/pricing screen patches price cells in open browsers |
13d | SSR + wo:live/wo:bind client runtime |
| Millions of readers + millions of live recipients | 13e | scale targets + load harness |
The scale story (13e)
How "one product, millions of customers" works — all of it existing design, instantiated for this demo:
- RAM-resident. The engine is the in-memory design of
03-inmemory-engine.md; the WAL/disk phases (10–12) sit behind the read path for durability, never in front of it. - Reads scale across cores. Thread-per-core, shared-nothing shards behind
SO_REUSEPORT(09-concurrency-scaleout.md). A hot product row is owned by one shard but read-replicated to every thread, refreshed by the same per-thread broadcast that feeds subscribers — soGET /api/products/1spreads over all cores with zero contention, while writes keep a single owner. - Live updates fan out in two stages. One
set_pricecommit → one delta message per thread (not per subscriber, per plan 09d) → each thread predicate-matches its local subscription table and batches socket writes on its own ring. Kernel primitives only: edge-triggered epoll today (done/02-event-loop-epoll.md), per-thread io_uring next (exploration/linux/07-io_uring.md).
Verification targets (1 M aggregate reads/s of one product on 16 cores, 1 M live subscribers with p99 delta delivery < 250 ms, commit→first-delta p99 < 10 ms) are defined in plan 13 § 13e.
Try it today
just pricing-demo # scripted CRUD round-trip, self-contained
# or:
cargo run --bin wo -- run docs/examples/pricing
# [wo] compiled catalog — 2 types
curl -X POST localhost:8080/api/products \
-H "Content-Type: application/json" -d '{"sku":"WO-001","name":"writeonce mug"}'
curl localhost:8080/api/products
Methods and live push are the next sub-phases (13b/13c). For the type-based minimal program, see ../hello/; for the full workspace shape, ../../examples/ecommerce/.