Defining a Specification as a Test Suite — "The Spec Is: It Passes roast"

Perl 6 had a three-layer design document system: Apocalypses, Exegeses, Synopses. And in the end it moved authority to a test suite called roast. A prose specification has three problems — interpretations diverge, it drifts from the implementation, and conformance cannot be checked. Tests solve those. But something is given up in exchange: prose speaks vaguely about everything, tests speak precisely about some things.

perlrakuroastspecificationtestinglanguage-designprogramming-languages

Last time I ended on my own language:

If the oracle itself is wrong, all five are wrong together.

This instalment is about how Perl 6 handled that question. On what basis do we say something is correct as Perl 6?

Three Layers of Design Document

Part 3 got as far as Larry Wall digesting the 361 RFCs. The output was the Apocalypses. In practice, there were three kinds of document.

Document Author Role Metaphor
Apocalypse Larry Wall Design decisions and their reasons: why Revelation
Exegesis Damian Conway Explains an Apocalypse with examples Commentary
Synopsis Various A summary usable as a spec; what implementers consult Summary

The names are all biblical, which is very much Perl culture.

Apocalypse numbers match the chapters of Programming Perl, the Camel Book: each answers “what happens to chapter N in Perl 6.”

Number Subject
A1 The Ugly, the Bad, and the Good — overall direction
A2 Basic data types, sigils, literals
A3 Operators
A4 Control structures
A5 Pattern matching — the regex redesign. The single most consequential one
A6 Subroutines — signatures, multiple dispatch
A12 Objects

Not all of them were written. That is why the numbers skip.

The Synopses Became the Working Spec

What implementers actually consulted day to day was not the Apocalypses but the Synopses (S01–S32).

The reason is plain. An Apocalypse is long prose explaining why. It is not suited to being looked things up in. The Synopses were organised to be consulted, and they kept being updated.

Number Subject
S02 Lexical structure, data types, sigils
S03 Operators (including meta-operators)
S05 Regexes and grammars
S06 Subroutines and multiple dispatch
S12 Objects
S14 Roles and types
S17 Concurrency
S32 Standard library

That is the era of the prose specification.

Three Problems With a Prose Specification

And that form has problems of its own.

1. Interpretations Diverge

When implementers read differently, implementations disagree. As part 5 covered, Perl 6 had several implementations in this period: Pugs, the Parrot-based one, and later Niecza on .NET.

Reading the same Synopsis and implementing different behaviour happens. Prose contains no procedure for deciding which is right.

2. It Drifts From the Implementation

If the document stops being updated, nobody notices.

This is the general problem with design documents. Documents do not execute. So when they are wrong, or stale, nothing turns red. It is the same structure as “unverified decisions accumulate, unverified” from part 5.

3. Conformance Cannot Be Checked

This is the most practical problem.

When an implementation claims to “conform to Perl 6,” there is no procedure for verifying the claim. Someone has to read the prose and decide.

roast — Moving the Spec Into Tests

So Perl 6 moved authority to a test suite called roast: the Repository Of All Spec Tests.

Its origin, as part 5 described, is the practice Audrey Tang started with Pugs — writing a test into the official suite for every feature implemented.

And eventually this was declared:

The specification is: it passes roast.

What “Being 6.c” Means

The 6.c release of 2015 — part 8’s subject — was defined by freezing a particular commit of roast.

So “conforms to 6.c” means “passes roast as of 6.c.”

That is startlingly concrete. The problem of interpreting prose disappears.

  • When an implementation claims 6.c conformance, that claim is mechanically checkable
  • A new version is defined as a new frozen point of roast
  • Old behaviour is reproducible, as long as the old roast is preserved

And the state described in part 12 — 6.d as default, 6.e in progress — simply means there are two frozen points and a third is being built.

What Was Gained, and What Was Given Up

Gained Given up
Conformance is mechanically checkable Behaviour not written into a test stays unspecified
Spec and implementation cannot drift apart Tests do not tell you why
Versions can be frozen (6.c / 6.d) Tests can quietly pin an implementation’s convenience

The first row on the right matters most. Here is this series’ eighth pattern.

Prose speaks vaguely about everything; tests speak precisely about some things.

A prose specification says something about the whole language — vaguely. A test specification decides exactly, where it is written. And says nothing at all where it is not.

This is not a ranking. It is that the shape of the silence differs.

  • Prose falls silent in a way you can see: “this part is vague”
  • Tests fall silent in a way nobody notices: “there is no test here”

Which is why, even after moving to roast, Raku did not abandon prose. The Synopses remain as historical documents, and the language documentation (docs.raku.org) carries the practical explanation. What moved to roast is authority.

How Other Languages Do It

This decision is not unique to Perl 6.

Language Authoritative spec
Raku roast (a test suite)
ECMAScript ISO/ECMA prose specification + test262 (shared tests)
Ruby The CRuby implementation + ruby/spec
C / C++ ISO standards (prose)
Go A language specification (prose) + a single reference implementation

ECMAScript is the closest. It has both prose and test262, and “conformance” is measured by test262.

What makes Raku unusual is that it kept the spec separate from the implementation through a long stretch of having only one. As part 6 covered, from the moment Niecza stopped, Rakudo was the only usable Raku.

Normally, with one implementation, “the implementation is the spec” suffices. That is Ruby’s position (CRuby is the de facto spec). Raku maintained roast anyway — partly as an inheritance from the era of several implementations, and partly because it is a breakwater that matters precisely once you are down to one.

And as the end of this section describes, in 2026 that inheritance paid off.

With a single implementation, an implementation bug can settle in as “the language’s specification.” With roast, you can ask: is that written in a test?

Back to Last Time’s Question

Part 6 ended here:

If the oracle itself is wrong, all five are wrong together.

roast solves that halfway.

The part it solves: the specification lives somewhere independent of the implementation. How Rakudo behaves and how Raku ought to behave are in different repositories. An implementation bug does not automatically become the spec.

The part it does not: it can say nothing about what is not written in roast. In terms of my own parity setup: the region where the interpreter is wrong and nobody has written a test for that behaviour is still undefended.

Perl 6 itself plugged that hole another way. It had several implementations. When Pugs, the Parrot-based one, and Niecza all run the same roast, behaviour present in only one of them is either a hole in the spec or a bug in the implementation — and you can tell.

A second implementation produces a kind of bug the first one cannot notice.

Part 6 said the second user never came. From the standpoint of verifying a specification, the departure of the second implementation is the same kind of loss.

From the moment Niecza stopped, Raku maintained roast with a single implementation for over a decade. Reasonable — but one means of verifying how much of the specification roast actually captures had been removed.

And in 2026, a second one came back

As this is being written, in 2026, two implementations other than Rakudo are running.

Implementation Language Approach Roast (files fully passing, of 1,464)
mutsu Rust Bytecode VM 1,433 (97.9%)
Raku++ (rakupp) C++17, no third-party deps Hand-written lexer and parser, tree-walking evaluator; also compiles to a native binary 676 (46%)

Both measure themselves with roast as the denominator. “Conforms to Raku” being a number rather than a claim is possible because the specification was moved into a test suite. This instalment’s subject is doing its work right here.

⚠️ Figures checked on 18 September 2026. Both projects move constantly; mutsu’s README states outright that compatibility improves daily. Re-measure before quoting.

So the worry in this section is partly resolved. The means of re-asking how much of the specification roast captures has returned after a decade.

A second implementation produces a kind of bug the first one cannot notice.

That pattern is usable again.


Next (part 8): Christmas. On 25 December 2015, fifteen years after the announcement, the first stable release arrives. What were those fifteen years? Time to add it up.

← Back to The Lineage of Perl and Raku