A language where the whole program moves with a clock.
Component based, general purpose, declarative language, inspired by hardware description languages. An Avantgarde program is not a sequence of instructions — it is a circuit. Running it means ticking it.
Under (re)specification, 2026 edition. Earlier attempts (2017–2018) produced the
sketches in spec/flow/ and a module system draft; the project then hibernated.
The restart is built around the one idea that was always the point and never got
written down properly: global clock semantics. New documents live in
design/, new examples in spec/clocked/.
No compiler yet, on purpose. Think first, then do :)
- Everything is a component. Inputs, outputs, a wiring diagram. No functions, no objects, no statements. You craft chips and plug them together.
- Time is explicit. The program advances in ticks. Within a tick, values flow through pure wiring; at the tick's edge, all registers update simultaneously. There is no "during" — so there are no read-modify-write bugs, no interleavings, and swapping two registers needs no temp variable.
- State is declared, not acquired.
state:blocks are the only memory in the language. No heap, no variables. Wires and registers. - Effects are wires. Components never perform IO; they emit requests on
effect pins. Only the top-level board file connects pins to real peripherals.
Don't connect the pin — the effect cannot happen. Afraid to launch a rocket
in Avantgarde? It is impossible (
spec/clocked/rocketGuard.av). - Concurrency is clock domains. A component on its own clock is an actor: private registers, message queues at the boundary, no shared instants, no races. Actors weren't added to the language; they fell out of refusing to lie about simultaneity.
- Tests are testbenches. Drive inputs over ticks, assert on output
waveforms, state temporal properties (
always,never,within). Failures replay exactly — determinism is total. - Specified down to Nand. The semantic model bottoms out at gates;
Numand friends are intrinsics that behave as if built from them. Nothing is magic, some things are merely fast.
package: Examples/Clocked
component: Counter
input: enable : Bit, reset : Bit
output: value : Num
state:
count : Num = 0
functionality:
+(a <- count, b <- 1, out -> incremented)
cond(if <- enable, then <- incremented, else <- count, result -> kept)
cond(if <- reset, then <- 0, else <- kept, result -> count)
id(a <- count, out -> value)
More: counter · blinker · traffic light FSM · echo · fibonacci in time · an actor · a testbench
The spec is written as a design journal with chapters — see the index. Highlights: 01-clock (read this one first), 05-effects, 07-domains, 08-testing.
Module system follows the 1ML lineage (components and modules are one kind of thing): https://people.mpi-sws.org/~rossberg/papers/Rossberg%20-%201ML%20--%20Core%20and%20modules%20united%20[JFP].pdf
Aren't you tired of the same problems everywhere? How to test, how to structure a project, which abstraction to pick, how to make the code comprehensible? Avantgarde is an opinionated bet: one primitive (the clocked component), one sequencing mechanism (the tick), one composition mechanism (wiring). As Go simplified by removing, Avantgarde simplifies by removing more — including the call stack. You will never need an AbstractFactory, an IOC container, a Monad or an MVC, and this time the README can point at the design docs that explain why.
Tracked honestly. Scoreboard as of July 2026 — the four questions the 2018 README left open are now all answered:
- Typeclasses vs 1ML modules? — resolved: no typeclasses. Signatures +
wiring-time
withbindings do the whole job (04-interfaces.md, epitaph in the journal graveyard). - What type system? — static, inferred,
Bitat the bottom, time in the types via wire/reg,Same(a)polymorphism (02-types.md). - Isn't circuit execution hopelessly slow? — the answer is change propagation: only the cone of influence of changed inputs re-settles, a quiet tick costs zero (13-execution.md). Unproven until benchmarked, but no longer unanswered.
- Effects as algebraic effect stacks? — yes, and better than hoped: a
handler stack is a series circuit, handlers are chips on the wire
(05-effects.md,
spec/clocked/logger.av).
The honest new open questions, one per scary:
- Closures. Helper components can't see parent wires; leaning toward nested components with lexical wire scope (journal, May 18). The one hole in the core design.
- Capacity inference.
List<t, cap>everywhere means "cannot OOM, by theorem" — if and only if inference keeps the caps out of your face (11-memory.md). - Sugar. The no-expressions rule is the language's soul and occasionally its tax (journal, Jun 21). A desugars-to-wires expression layer is now scheduled before the parser, not after.
- Smaller ones live as
-- open:notes inside each design doc, on purpose — a question belongs next to the thing it doubts.
If you have design suggestions, open an issue. Some of the old specs are out of date, so be careful.
