( ideas -- software )
A strongly-typed, actor-native, stack-based language on the BEAM.
ActorForth is a self-hosted, strongly-typed, stack-based language on the Erlang BEAM VM, where actors are primitives and type-driven dispatch delivers correctness-by-construction. Every word declares its exact type transformation, and the system enforces it. Its concatenative design eliminates variable naming, nesting, and boilerplate, lowering cognitive load for both humans and LLMs. Programs convey full intent in a fraction of the context conventional languages demand. Define four types and you have a compiler. The language is built for creating domain-specific languages where smart contracts and critical systems are safe by design.
Try it ( repl -- )
Clone the repo, build with rebar3, start the REPL. Define fib, the traditional first ActorForth program:
$ git clone https://github.com/ActorForth/ActorForth
$ cd ActorForth
$ rebar3 shell
1> af_repl:start().
a4> 2 3 + .
5
a4> : fib 0 Int -> Int ; drop 0 .
a4> : fib 1 Int -> Int ; drop 1 .
a4> : fib Int -> Int ; dup 1 - fib swap 2 - fib + .
a4> 10 fib .
55
A standalone a4c CLI is landing soon via the bundled Zig wrapper. Single binary. Cross-platform.
Four ideas ( -- why-this-matters )
Actors as primitives
Not a library, not a framework. Any typed instance can become an actor with the server word, reachable through a dedicated send-protocol. Supervision trees, message passing, selective receive, hot code loading. It is Erlang underneath, exposed as first-class syntax.
Correctness by construction
Every word declares its exact stack signature. Multi-clause dispatch matches on both type and value, so base cases are not if statements. They are separate, type-checked clauses. The type system does not run after your code. It is what decides which code runs.
Built for DSLs
The compiler is itself four types: WordDefinition, InputTypeSignature, OutputTypeSignature, CodeCompile. Their handlers change what tokens mean while those types sit on top of the stack. Define four more and you have defined your own syntax. No macros. No AST surgery.
Self-hosted, all the way down
The parser, compiler, code generator, and build driver are written in ActorForth and compile themselves to BEAM. Ring 0 is an eighty-op virtual machine. Ring 1 emits abstract BEAM forms. Ring 2 is the A4 compiler. The fixed point closes.
The language, briefly ( code -- intuition )
Fibonacci, written as three clauses. The two base cases pattern-match on value constraints in the stack signature. The recursive clause handles every other integer. There is no conditional:
: fib 0 Int -> Int ; drop 0 .
: fib 1 Int -> Int ; drop 1 .
: fib Int -> Int ; dup 1 - fib swap 2 - fib + .
10 fib . # 55
A product type declares its fields. The constructor, non-destructive getters, and setters are generated automatically:
type Point
x Int
y Int
.
3 4 point # creates a Point on the stack
x # 3 (leaves the Point in place)
y # 4
An actor is a typed instance with private state, addressed by a dedicated send-protocol:
type Counter count Int .
: inc Counter -> Counter ; count 1 + count! .
: get Counter -> Int ; count .
0 counter server
<< inc inc inc get >> . # 3
Tokens between << and >> that are part of the actor's vocabulary dispatch to it. Everything else runs locally. Auto-generated setters are not exported, so state stays private without you writing a line of encapsulation code.
Why this, why now ( -- rationale )
Software failure is most expensive in the industries where mainstream language ergonomics are least suited to formal reasoning: smart contracts, safety-critical systems, financial infrastructure. Inheritance hierarchies, mutable fields, and implicit control flow produce programs that are hard for humans to verify and harder still for tools to reason about. These are not catastrophic failures of key systems. They are small, innocuous defects that only manifest in rare edge conditions, and they compound geometrically as systems grow.
ActorForth takes a different position. A concatenative surface with strong types, dispatch-by-signature, and actor concurrency as a primitive produces programs that are dense in intent and shallow in accidental complexity. Programs tractable to both human review and machine analysis.
The concatenative form has a second, perhaps unexpected property. It is compact. A word is defined by its stack signature and a sequence of other words. No identifier invention, no pronoun tracking, no scope traversal. For an LLM reading or writing code, that means far more semantic intent per token of context. We are not claiming empirical studies. We are saying the geometry of the language makes this plausible, and we invite you to see for yourself.
Get involved ( curiosity -- contribution )
- Source and documentation on GitHub
- Project changelog. The story from 2018 to today, across three implementations
- Language definition
- Intro to ActorForth, in the spirit of Brodie's Starting FORTH
- Architectural drivers and design considerations
- Issues and discussion