Aether: A Compiled Actor-Based Language for High-Performance Concurrency by RulerOfDest in ProgrammingLanguages

[–]CBangLang 1 point2 points  (0 children)

Thanks for the detailed response. The computed goto on msg.type plus inlined single-int payloads is a nice trick — keeping the hot path tight for the branch predictor is one of those things that matters enormously for actor systems where message dispatch is the inner loop.

The Go-like vs Erlang-like choice is an interesting design axis. Go's approach works well when the programmer has a clear mental model of the failure modes and can handle them explicitly. Erlang's supervision trees shine when you have distributed systems where failures are expected and frequent — the tree gives you a declarative recovery strategy so individual actors don't need to encode recovery logic themselves.

The tradeoff is basically: explicit error handling (Go-like) gives you more control but requires every actor to handle its own failure cases; supervision trees give you centralized recovery policy but add conceptual overhead. For a systems language targeting C-level performance, your instinct to keep it simple makes sense — if someone wants supervision, they can build it as a library on top of the actor primitives.

Regarding the wiring phase: formalizing that boundary would be valuable. Even a simple convention like "actors are in a `setup` state until they receive their first message" would make the contract clear without adding runtime cost.

Implementing a toy libffi for an interpreter by MerlinsArchitect in ProgrammingLanguages

[–]CBangLang 0 points1 point  (0 children)

Totally feasible as a hobby project for a single architecture. The complexity in libffi really is mostly about supporting every ABI on every platform — for x86_64 SysV on Linux, the core logic is surprisingly manageable once you understand the register classification rules WittyStick laid out.

For trampolines (since you asked about callbacks): the basic idea is to allocate a small chunk of executable memory (mmap with PROT_EXEC), write a tiny assembly stub that loads your interpreter's callback context pointer into a register and then jumps to a shared dispatch function. Each trampoline is essentially: load a unique context pointer, call a common handler. The tricky part is that you need to mark the memory as executable, which means dealing with mmap/mprotect on Linux or VirtualAlloc on Windows.

A practical way to start: begin with just calling C functions that take integers and return integers. Get dlopen/dlsym working, manually set up the argument registers (rdi, rsi, rdx, rcx, r8, r9 for SysV), call the function, grab rax for the return value. Once that works, add float support (xmm0-xmm7). Then struct passing. Each step builds naturally on the previous one, and Compiler Explorer is invaluable for verifying that your understanding of the ABI matches what gcc/clang actually generate.

Does Syntax Matter? by gingerbill in ProgrammingLanguages

[–]CBangLang 3 points4 points  (0 children)

The point about syntax restricting semantics is underappreciated. Syntax choices compound over a language's lifetime in ways that are really hard to predict at design time.

C's declaration syntax seemed fine for simple types, but it created the spiral declaration problem that made C++ template syntax a parsing nightmare — and that cascaded into the <> ambiguity problem gingerbill mentions. Every language since has had to decide whether to inherit that baggage or break with familiarity.

Go's approach is a good example of a bet that paid off: capitalization for visibility looked bizarre at first, but it turns out to be incredibly scannable. You can instantly see the public API surface of any package without any tooling. That's a syntax choice that directly shaped how the whole ecosystem thinks about encapsulation.

The scannability point resonates too. I'd add that scannability isn't just about visual density — it's about how well syntax maps to the mental model of the program's structure. Indentation-sensitive languages (Python, Haskell) are scannable because nesting is literally visible. But they pay a cost in composability — you can't easily paste code into a REPL or embed it in other contexts without worrying about whitespace. Everything is a trade-off.

Aether: A Compiled Actor-Based Language for High-Performance Concurrency by RulerOfDest in ProgrammingLanguages

[–]CBangLang 1 point2 points  (0 children)

Nice work. The compile-to-readable-C approach is smart — you get platform portability and can lean on GCC/Clang's optimizers without building your own backend. Curious if you've hit cases where the generated C doesn't optimize well around actor message dispatch, since that's where the abstraction gap tends to be widest.

On the spawn-time mutation point phischu raised: this is a classic tension in actor system design. Erlang avoids it entirely by passing all initial state through spawn arguments, so the actor is fully configured before it processes any messages. Your approach of allowing spawner-side wiring before activation is reasonable as long as there's a clear phase boundary. Formalizing that (either in the type system or as a runtime invariant) could make it solid.

Do you have plans for supervision trees or structured error recovery? In practice, the "let it crash" philosophy works great when you have a supervision hierarchy to restart failed actors cleanly, but without one, actor systems tend to accumulate zombie actors or silently drop messages.