You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rollup of a comparison between Wyzer (Wyzer-Lang/Wyzer, surveyed at the in-tree snapshot ../wyzer/, HEAD 2fb83ca, 2026-08-07) and Aether, plus an assessment of which Wyzer ideas are worth pulling in. Filed for visibility / triage.
Unlike the Rux rollup (#581), this one does contain a big idea worth taking seriously: choreographic programming. Most of the rest of Wyzer is far behind us and not worth copying.
TL;DR
Wyzer is early (~5,100 LOC of OCaml, ~62 test programs, no CI, no Vec/HashMap/std::net yet) but it is built around a genuinely novel idea we have no answer to: declare the machines, write one program, let the compiler split it and insert the network calls — with location carried in the type system so cross-machine data can't be silently used locally.
The actionable takeaway is not "build choreography." It is one specific piece: location-typed values, which is a natural extension of the session-protocol work already proposed in #385.
Wyzer vs Aether — high-level
Dimension
Aether
Wyzer
Backend
Transpiles to readable C; inherits the C optimizer; runs anywhere with a C compiler
LLVM IR (lib/codegen.ml) + a tree-walking interpreter (lib/eval.ml)
Implementation
C (~7k LOC compiler, ~42.5k runtime+std)
OCaml (~5.1k LOC total)
Maturity
Since 2025-03, 3,234 commits, real downstream users (avn, aether-ui, aeb)
~months old, 10 PRs
Memory model
Manual + compiler-inserted heap tracking, defer
Perceus refcounting with in-place reuse when uniquely owned
Concurrency
Actors (spawn/receive/!/?), in-process
Choreographic roles; std::thread not implemented yet
Defining feature
Capability sandboxing, C interop/embedding, compile-to-C portability
Choreography: role @Server / role @Sensor, endpoint projection, location-typed values
Stdlib
69 std + 6 contrib modules
5 working (io, math, time, process, string); fs/net/collections/thread unchecked in their TODO
Tests / CI
984 .ae programs + C unit suite; 23-job matrix (Linux/macOS/Windows/FreeBSD/RISC-V/WASM/Cortex-M4, valgrind, ASan)
62 .wyz files; no CI
The one idea worth taking: location-typed values
Wyzer's choreography rests on three parts. Only the middle one is worth lifting on its own.
Role declarations + endpoint projection — role @Server; at the top, fn get_temperature() @ Sensor, and a compiler pass (lib/projection.ml, 241 LOC) that strips the AST per role behind a --role CLI flag. Interesting, but it presumes a whole-program, single-file world that doesn't match how Aether programs are built.
Location in the type system. ⭐ If the Sensor returns a u32, its type is u32@Sensor, and using it on the Server requires an explicit transfer(). Functions with no role annotation infer as Poly and are guaranteed not to touch node-specific globals. This is the part with real value independent of the rest, and it maps onto Aether directly: today an actor_ref says what messages exist, and Actor session protocols for compile-time message sequence checks #385 proposes adding when each is legal. Neither says where the actor is — so nothing stops code treating a remote actor's reply as a local value, which is precisely the bug class that bites when actors stop sharing an address space.
Implicit RPC on plain function calls. Calling a @Sensor function from @Server silently becomes a network round-trip. I'd argue against copying this: it hides latency and failure behind syntax that looks free, which is the classic distributed-systems footgun (cf. Waldo's A Note on Distributed Computing). Aether's explicit ! / ? is the better default — the send is visible.
#385 already proposes protocol state on an actor reference (actor_ref<FileWriter>, rejecting Write after Close). Location is the same kind of compile-time annotation on the same type:
Both are analyses over sends through typed references; both want the same escape hatches for dynamic refs and FFI. Designing them together seems much cheaper than bolting location on later, and it would give the three open distributed-actor proposals (#423 ephemeral identity, #424 downhill load balancing, #426 reliable byte streams) a type-level foundation they currently assume but don't have.
Suggested scope if pursued: a design spike only — no implementation commitment. Concretely: can an actor_ref carry a location parameter (local / a named node / any), can the compiler reject using a remote reply as a local value without an explicit materialization step, and does that stay readable next to existing spawn/!/? code. If the answer to any of those is ugly, the idea dies cheaply.
Also worth a look (smaller)
Perceus refcounting with in-place reuse. Wyzer gets no-GC automatic memory management and in-place mutation when the refcount is 1. Not a realistic swap for Aether's model at this stage, and it would collide with the "runtime stays C" line — but the in-place-when-unique optimization is worth knowing about as a codegen idea. Reference: lib/perceus.ml (188 LOC) is small enough to read in one sitting.
Compile-time-checked physical transport.role @Sensor { transport: "I2C", address: 0x48 } — declaring the hardware channel in the role and having the compiler verify it. We have nothing like this; it's a neat fit for an embedded story given we already build for Cortex-M4.
Aether already has a better version — do NOT copy
Backend. LLVM buys optimization for free, but compile-to-C is why we run on FreeBSD, RISC-V, WASM and Cortex-M4 from one codebase. Don't re-litigate.
Stdlib, FFI, testing, CI. Not close, and not the interesting part of the comparison.
Implicit RPC. See above — actively worse than explicit !/?.
Caveats on this survey
Assessed from Wyzer's docs, TODO.md checkboxes, and source layout — not by building or running it. TODO checkboxes can lag reality in either direction. Two specifics worth verifying before leaning on anything here: std::net is unchecked in their TODO, and "export physical standalone binaries for projected nodes" is also unchecked — so the choreography feature appears to be real in the type system and projection pass but not yet end-to-end over a real network. The idea is the valuable part, not the implementation.
Companion doc following the docs/cross-references/ convention to follow if this is worth keeping discoverable.
Rollup of a comparison between Wyzer (
Wyzer-Lang/Wyzer, surveyed at the in-tree snapshot../wyzer/, HEAD2fb83ca, 2026-08-07) and Aether, plus an assessment of which Wyzer ideas are worth pulling in. Filed for visibility / triage.Unlike the Rux rollup (#581), this one does contain a big idea worth taking seriously: choreographic programming. Most of the rest of Wyzer is far behind us and not worth copying.
TL;DR
Wyzer is early (~5,100 LOC of OCaml, ~62 test programs, no CI, no
Vec/HashMap/std::netyet) but it is built around a genuinely novel idea we have no answer to: declare the machines, write one program, let the compiler split it and insert the network calls — with location carried in the type system so cross-machine data can't be silently used locally.The actionable takeaway is not "build choreography." It is one specific piece: location-typed values, which is a natural extension of the session-protocol work already proposed in #385.
Wyzer vs Aether — high-level
lib/codegen.ml) + a tree-walking interpreter (lib/eval.ml)deferspawn/receive/!/?), in-processstd::threadnot implemented yetrole @Server/role @Sensor, endpoint projection, location-typed valuesstd+ 6contribmodulesio,math,time,process,string);fs/net/collections/threadunchecked in their TODO.aeprograms + C unit suite; 23-job matrix (Linux/macOS/Windows/FreeBSD/RISC-V/WASM/Cortex-M4, valgrind, ASan).wyzfiles; no CIThe one idea worth taking: location-typed values
Wyzer's choreography rests on three parts. Only the middle one is worth lifting on its own.
Role declarations + endpoint projection —
role @Server;at the top,fn get_temperature() @ Sensor, and a compiler pass (lib/projection.ml, 241 LOC) that strips the AST per role behind a--roleCLI flag. Interesting, but it presumes a whole-program, single-file world that doesn't match how Aether programs are built.Location in the type system. ⭐ If the
Sensorreturns au32, its type isu32@Sensor, and using it on theServerrequires an explicittransfer(). Functions with no role annotation infer asPolyand are guaranteed not to touch node-specific globals. This is the part with real value independent of the rest, and it maps onto Aether directly: today anactor_refsays what messages exist, and Actor session protocols for compile-time message sequence checks #385 proposes adding when each is legal. Neither says where the actor is — so nothing stops code treating a remote actor's reply as a local value, which is precisely the bug class that bites when actors stop sharing an address space.Implicit RPC on plain function calls. Calling a
@Sensorfunction from@Serversilently becomes a network round-trip. I'd argue against copying this: it hides latency and failure behind syntax that looks free, which is the classic distributed-systems footgun (cf. Waldo's A Note on Distributed Computing). Aether's explicit!/?is the better default — the send is visible.Why this fits #385, not a separate feature
#385 already proposes protocol state on an actor reference (
actor_ref<FileWriter>, rejectingWriteafterClose). Location is the same kind of compile-time annotation on the same type:Both are analyses over sends through typed references; both want the same escape hatches for dynamic refs and FFI. Designing them together seems much cheaper than bolting location on later, and it would give the three open distributed-actor proposals (#423 ephemeral identity, #424 downhill load balancing, #426 reliable byte streams) a type-level foundation they currently assume but don't have.
Suggested scope if pursued: a design spike only — no implementation commitment. Concretely: can an
actor_refcarry a location parameter (local/ a named node /any), can the compiler reject using a remote reply as a local value without an explicit materialization step, and does that stay readable next to existingspawn/!/?code. If the answer to any of those is ugly, the idea dies cheaply.Also worth a look (smaller)
lib/perceus.ml(188 LOC) is small enough to read in one sitting.role @Sensor { transport: "I2C", address: 0x48 }— declaring the hardware channel in the role and having the compiler verify it. We have nothing like this; it's a neat fit for an embedded story given we already build for Cortex-M4.Aether already has a better version — do NOT copy
!/?.Caveats on this survey
Assessed from Wyzer's docs,
TODO.mdcheckboxes, and source layout — not by building or running it. TODO checkboxes can lag reality in either direction. Two specifics worth verifying before leaning on anything here:std::netis unchecked in their TODO, and "export physical standalone binaries for projected nodes" is also unchecked — so the choreography feature appears to be real in the type system and projection pass but not yet end-to-end over a real network. The idea is the valuable part, not the implementation.Companion doc following the
docs/cross-references/convention to follow if this is worth keeping discoverable.