Four and a Half Years Without an Implementation, and One Year of Pugs — A Perl 6 Written in Haskell

On 1 February 2005, Audrey Tang started writing a Perl 6 implementation in Haskell. Pugs. Four and a half years after the announcement, the design documents had piled up but nothing ran. Pugs got a substantial part of the language working within months — and left behind something larger than the implementation: the practice of writing a test into the official suite for every feature implemented. That grew into roast, and it rewrote what a specification meant for Perl 6.

perlrakupugshaskellhistorytestingprogramming-languages

Four and a Half Years With Nothing That Ran

Last time I laid out the nine things Perl 6 set out to change — each a language’s worth of work, and none of them independent of the others.

So what was happening during that time?

From the announcement in July 2000 until early 2005, Perl 6 had no implementation you could practically use. There were design documents. Apocalypses were written, Synopses organised, and Parrot (next instalment’s subject) was under development. But an environment in which you could write a Perl 6 program and run it did not, in any real sense, exist.

Four and a half years.

It is worth saying what that period meant.

A stretch that runs on design documents alone has a hazard of its own.

  • The more you write, the more it looks like things have been decided
  • But what you can only learn by actually writing it keeps piling up unlearned
  • And since nobody is writing it, nobody notices it is still there

Documents accumulate. It looks like progress. Unverified decisions accumulate, unverified.

1 February 2005

Audrey Tang started writing a Perl 6 implementation. The language was Haskell. The project was Pugs (Perl 6 User’s Golfing System).

Why Haskell

Several reasons.

1. Parser combinators. With Parsec you can implement a complex grammar fast. Perl 6’s grammar is large. Speed here was decisive.

2. Laziness lines up with Perl 6. The lazy lists and infinite sequences from part 4 are Haskell’s default behaviour. The implementation comes out straightforward.

3. Algebraic data types make ASTs easy to write.

And probably the largest one:

4. It was a language Audrey Tang could write quickly in.

There is an important judgement embedded here: dropping the premise that Perl 6 had to be implemented in Perl.

The prevailing assumption at the time was that a Perl 6 implementation would be built on Parrot. Pugs ignored that. It put “get something running as fast as possible” ahead of everything else.

What Happened — Speed

By the accounts of people there, Pugs moved extraordinarily fast. Within months a substantial part of Perl 6 ran, and it became the first environment in the world where you could practically write Perl 6.

Four and a half years with nothing. Then, in a year, something.

That contrast is very nearly the whole of Pugs’s historical meaning.

Three Things Pugs Left Behind

Pugs itself is not in use today; the repository is archived. It is still treated as the turning point in Perl 6’s history because of what survived as practice.

1. Spec Tests — the Origin of roast

Audrey Tang started a practice: for every feature implemented in Pugs, write a test into the official test suite.

Why this matters.

There can be many implementations. Pugs, the Parrot-based one, and others later. But the tests are shared. A test does not say “Pugs behaves this way”; it says “Perl 6 behaves this way.”

That practice grew into roast (Repository Of All Spec Tests), and eventually into the position that roast is the specification. Part 7 takes that up.

2. Implementation Fed Back Into Design

Actually writing it in Pugs surfaced concrete places where the Synopses did not decide, or decided something awkward to implement. Those went back into the Synopses.

Here is the fifth “pattern” of this series.

Implementation produces a different kind of error than reading does.

Five years of design documents, and things were still only discoverable by writing it. They were not the kind of thing another read-through would have found.

3. A Community Crossing Over

People from the Haskell community started working on Perl 6.

Pugs became an entry point in both directions — Perl people learning Haskell, Haskell people encountering Perl 6 — and it brought type-system vocabulary into Perl 6’s design conversation.

Audrey Tang was also known in this period for handing out commit bits very freely: send a patch, get commit access. It lowered the barrier to participation enormously.

That is partly a side effect of having something that runs. When something runs, people can make small contributions. A design document has no small contributions to make.

Slowdown, and the End of a Role

Pugs development slowed from around 2007. The immediate cause was Audrey Tang stepping back from the front line, for health and other reasons.

⚠️ This should not be written beyond what is publicly stated. What can be said as fact is that one near-full-time contributor leaving hit the project’s velocity directly.

In part 3 I listed “few full-time hands” as the fourth reason it took fifteen years. Pugs is the sharpest example. One person’s departure changed the pace of the whole project.

(Audrey Tang later served as Taiwan’s Digital Minister.)

Where It Sits

Pugs is not a failed implementation. It is the implementation that rewrote the relationship between specification and implementation.

Before Pugs After Pugs
Design documents (Synopses) are authoritative The test suite (roast) becomes authoritative
Implementations follow the spec Implementations sometimes move the spec
Perl 6 is a thing on top of Parrot There may be many implementations

And one more thing.

Pugs proved the value of having something that runs. Four and a half years of design, one year of implementation. The latter changed the mood. This does not mean the design was wasted — Pugs was fast because the Synopses existed. You need both; but one alone does not move.

Next time, the other half goes badly wrong. Perl 6 lost a bet, once, on its implementation substrate.


Next (part 6): The bet on a general-purpose VM. Parrot aimed to be the shared VM for dynamic languages, and ended up nobody’s. What did MoarVM give up in order to finish?

← Back to The Lineage of Perl and Raku