Est. 2005 Intermediate

Amber for Parrot

Amber for Parrot was an Eiffel-and-Ruby-flavoured scripting language for the Parrot virtual machine, built by Roger Browne in 2005 to prove that Design by Contract belonged in a dynamic scripting language.

Created by Roger Browne

Paradigm Object-oriented and contract-based scripting: classes with single- and multiple-inheritance, preconditions, postconditions and class invariants, plus anonymous agents (blocks) in the Ruby tradition and a script-file form with no boilerplate
Typing Dynamic - objects are Parrot PMCs at runtime; the compiler performs some validity checking, and assertions are checked at runtime only when enabled with the --check option
First Appeared 2005
Latest Version 0.4.2 "Argument" (28 February 2006)

Amber for Parrot was a short-lived scripting language written by Roger Browne between May 2005 and February 2006, targeting the Parrot virtual machine that the Perl 6 project was building at the time. Its premise was unusual and, in 2005, close to heretical: that a scripting language could be as convenient as Ruby while still carrying Eiffel’s Design by Contract machinery - preconditions, postconditions, class invariants, loop variants - as first-class syntax rather than as a testing library bolted on afterwards.

It never finished. The last release, 0.4.2 in February 2006, estimated its own progress towards 1.0 at 47 percent of the intended language constructs and 6 percent of the intended libraries. But Amber is a useful fossil, because it is one of the clearest surviving examples of what Parrot’s multi-language promise actually looked like when someone took it seriously.

Not to be confused with: Amber Smalltalk (a Smalltalk that compiles to JavaScript), or the modern Amber language that compiles to Bash at amber-lang.com. All three are unrelated projects that happen to share a name.

History and Origins

Roger Browne was a long-standing figure in the Eiffel community - the operator of Eiffelzone, a contributor to Eiffel tooling, and a regular at British Isles Eiffel user group meetings. Amber grew out of a specific frustration visible throughout its documentation: Eiffel had the best story in the industry about software correctness and reuse, and essentially none of the convenience that was making Perl, Python and Ruby the languages people actually reached for.

Parrot offered a way out. It was designed as a register-based virtual machine for dynamic languages, with a garbage collector, a rich object model built on PMCs (Polymorphic Containers), and an explicit ambition to host many languages at once and let them call each other. Writing a language for Parrot meant not having to write a runtime, an object system, a regular-expression engine or a GC.

The first release, 0.2.0 “Proof of concept”, appeared on 16 May 2005. Browne announced 0.2.1 “Hack Pie” to the perl6-internals mailing list on 5 June 2005, describing it as a scripting language whose syntax and semantics were largely inspired by Eiffel, with “anonymous agents” that behaved like Ruby blocks. The release was Linux-only, required Parrot 0.2.1, and was built with SmartEiffel 2.1 - the Amber compiler was itself written in Eiffel.

That last detail created an immediate distribution problem, and its solution is characteristic of the project’s practicality. Five days later, release 0.2.1b shipped the SmartEiffel-generated C code alongside the Eiffel sources, so that someone who wanted to try Amber did not first have to install an Eiffel compiler. The project’s own installation instructions ranked “install Amber from a C package” as the easiest route for most people.

The pace through the rest of 2005 was brisk - nine releases between May and December - and the version numbers were not vanity: Amber’s version tracked the Parrot version it targeted. Amber 0.4.2 was built against Parrot 0.4.2, and appeared roughly a week after it.

Design Philosophy

The project published a philosophy page, and it is worth reading as a period document of what a working Eiffel programmer thought scripting languages had got right.

Simple tasks should be easy. The page opens by conceding that scaffolding may be acceptable for a large application, but that hello world should be a one-liner:

print_line("Hello, world!")

There is no class, no main routine, no import. An Amber script file was defined as one or more instructions, optionally followed by feature declarations between private and end, optionally followed by class declarations - so a script grew into a program by accretion rather than by rewrite.

Readability over writability. “A line of code gets written once, but may need to be read and maintained many times by many people” - a straight inheritance from Eiffel’s house style, and the stated justification for keyword-heavy block structure over punctuation.

Interoperate, even at the cost of purity. Explicitly: “It’s important to be able to work with other Parrot-based languages. If this means using some standard Parrot idioms rather than a purer Amber-only solution, it’s worth it.” Very few of Parrot’s guest languages committed to this in writing, and fewer still followed through as far as Amber did.

Counting should be natural. “If you have three apples, you count them 1, 2, 3.” Amber arrays and strings were one-based - and the project’s own to-do list acknowledged the resulting friction, promising “some way … to conveniently interface Amber’s one-based arrays with languages that support only zero-based arrays.”

Freedom from semicolon slavery. Newlines terminated instructions; semicolons were optional separators for multiple instructions on one line.

Key Features

FeatureIn Amber for Parrot
Assignment:=, pronounced “is assigned”; result is the keyword for a function’s return value
ContractsPreconditions, postconditions (including Eiffel’s old from 0.4.0), class invariants, check instructions, loop variants and loop invariants
Assertion checkingOff by default from 0.3.1 onwards; enabled with --check, the AMBER_OPTIONS environment variable, or a #-prefixed directive at the top of a script
LoopsA single construct, loop ... repeat, with until clauses permitted at the top, middle or bottom - and more than one of them, giving multi-exit loops
Conditionalsif/elseif/else/end instructions plus if-expressions, and an inspect case instruction from 0.3.1
AgentsInline anonymous routines used as blocks and iterators, with read-only access to outer lexicals from 0.4.0
InheritanceMultiple inheritance, without Eiffel’s feature adaptation; the documentation describes using the same method resolution order as Perl 6 and Python 2.3 onwards
Exceptionsdo ... rescue ... retry ... recover ... finally ... end; ANY.raise for custom exceptions
Parrot interopexternal parrot / external pmc class declarations, routine bodies written in PIR, include of .pir files, load of .pbc bytecode, and dynamic library loading
Regular expressionsClass RULE, built on Parrot’s Perl 6 Rules engine
Source suffix.am

The loop

Amber’s most distinctive syntactic decision, and the one that drew the most argument in the comments on its own website, was the loop. Eiffel puts initialization in a from clause and the exit test at the top. Amber dropped the from clause - initialization simply happens before the loop - and let until appear anywhere in the body, as many times as needed:

loop
   read_line
until end_of_input
   do_something_with(last_line)
repeat

The rationale given: flexible exit positions remove code duplication and eliminate if instructions that exist only to break out of a loop. The repeat terminator - rather than Eiffel’s end - was defended on the grounds that loop ... repeat reads better for an infinite loop, and Browne noted in a 2005 reply that the keyword was “not yet cast in stone.”

Wrapping Parrot

The interoperability story is the part of Amber that still reads as ambitious. A kernel class was typically a thin Amber shell over a Parrot PMC:

class INTEGER
external pmc @@Integer
public
   -- queries
   abs
      -- Absolute value
      ...
public
   -- iterators
   times(proc)  -- proc: AGENT
      ...
end

The @@ prefix denoted a Parrot (non-Amber) class name and a single @ a Parrot feature name - notation that replaced an earlier, uglier {{...}} and {...} bracketing in release 0.2.3a. From version 0.3.1 the kernel classes ARRAY, BOOLEAN, CHARACTER, INTEGER, STRING and TABLE were all implemented as Parrot PMCs, which meant an Amber string and a string from another Parrot language were the same object rather than two things needing conversion. Individual routines could be written directly in PIR, or marked external parrot and loaded at runtime from Parrot bytecode.

Evolution

The arc of the release notes is a compressed portrait of building a language on a moving foundation. Early releases add language constructs at speed. Middle releases spend their effort on the runtime boundary - moving functionality out of a hand-written kernel.pir and into PMC-backed kernel classes, three alternative kernel implementations tested along the way. Later releases start reporting bugs that could be on either side of the Amber/Parrot line, which is what happens when your language’s semantics are your host VM’s semantics.

Parrot itself was the problem as much as the platform. It was under heavy, incompatible development throughout this period; the Amber download page warned that “at times, you will require the latest svn version of Parrot in order to use the latest darcs version of Amber,” and release 0.3.0 records capitalising Current, Result, Void, True and False purely to keep a SmartEiffel beta happy. Amber was chasing two moving targets - Parrot and SmartEiffel - with one part-time developer.

The unimplemented list at the end tells you where it stopped. Deferred (abstract) classes were still planned, possibly integrated with Perl 6-style roles. Floating-point and bignum support was “coming.” Multidimensional arrays were partial. Short-form and flat-form documentation tools, standard equipment for Eiffel programmers, were not written. Windows support was noted as “should not be hard” given that both SmartEiffel and Parrot were cross-platform - but according to its own documentation Amber was developed and tested only on Linux, specifically Fedora Core 4, and no Windows release ever appeared. And the kernel, in the author’s own words, was “just a skeleton. Many of the classes are only stubs, with just enough features to test interaction with the Parrot runtime.”

After 0.4.2 the project changed direction rather than continuing. By early 2007 the site had been renamed from “Amber for Parrot” to “the Amber Scripting Language,” and described the Parrot version as “an earlier version,” with development moved to a self-contained interpreter that would not depend on Parrot at all. That interpreter was never released. Snapshots of the site from 2008 and 2010 are unchanged: the same announcement of a self-contained interpreter under development, the same release history ending at 0.4.2, and the same wry status note that “it’s likely that a stable version of the Amber Scripting Language will be released this century.”

Current Relevance

Essentially none, in practical terms. The xamber.org domain no longer resolves; the darcs repository it hosted is gone; Amber does not appear among the actively maintained languages listed on Parrot’s own wiki; and Parrot itself, after reaching 8.x, ceased to be the target of the project that created it when Rakudo moved to MoarVM. Running Amber today would mean reconstructing a 2006 Parrot build, a SmartEiffel-generated C package, and the Fedora-era toolchain around both.

What survives is documentary. The release notes, the philosophy page, the interoperability guide and the language summary are all preserved in the Internet Archive, and together they form an unusually complete record of a one-person language project from the Parrot era - including the honest quarterly-style progress percentages that most language projects would never publish.

Why It Matters

Amber is worth knowing about for three reasons.

It took Parrot’s central claim literally. Parrot’s pitch was that many dynamic languages could share one runtime and interoperate freely. Most guest languages treated Parrot as a compilation target and stopped there. Amber wrote a philosophy point committing to Parrot idioms over language purity, then built its entire type system out of Parrot PMCs so that its integers and strings were everyone else’s integers and strings. That is the strong form of the interoperability argument, and it is instructive that the strong form was rare.

It asked whether contracts belong in scripting. In 2005 Design by Contract was firmly associated with large, statically typed, compiled systems, while scripting was associated with speed of writing and testing after the fact. Amber’s bet was that the two were not opposed - and its treatment of assertion checking as a runtime option, off by default, prefigures how contract-checking and validation layers are commonly deployed today. Modern languages have arrived at parts of this from other directions, but Amber argued it explicitly, from the Eiffel side, in a scripting language.

It is an honest record of why small language projects die. Nothing about Amber failed dramatically. There was no dispute, no better competitor, no abandoned funding. There was one developer, a host VM changing underneath him, a compiler toolchain also changing underneath him, a library that was 6 percent written, and a growing gap between the language’s ambition and the hours available. The release notes make that arc legible in a way that a repository of merged pull requests rarely does - which is a good reason to keep them, and the language, on the record.

Timeline

2005
Release 0.2.0, named "Proof of concept", is published on 16 May. It already carries the shape of the finished design: script files, classes, inheritance without feature adaptation, preconditions, postconditions and class invariants, loop variants and invariants, inline agents and iterators, routine bodies written in either Amber or embedded Parrot Intermediate Representation, and wrappers around Parrot's String, Integer, Boolean and Random classes - 24 test scripts and four examples in total
2005
Roger Browne announces "amber for parrot 0.2.1" (codenamed "Hack Pie") to the perl6-internals mailing list on 5 June, describing a proof-of-concept scripting language for Parrot with syntax and semantics inspired by Eiffel, plus anonymous agents that work like Ruby blocks. The release requires Parrot 0.2.1, targets Linux, and is built with SmartEiffel 2.1
2005
Release 0.2.1b on 10 June ships SmartEiffel-generated C code alongside the Eiffel sources, so that users without an Eiffel compiler can still build the Amber compiler. The language is discussed on Lambda the Ultimate the same year under the heading "Amber: Eiffel/Ruby inspired language for the Parrot VM"
2005
Release 0.2.2 "Gnuke" (6 July) adds expression-bodied routines of the form 'is <Expression> end', triple-quoted multiline string constants, and a first working version of the do/rescue/retry/recover/finally exception machinery. The Eiffel-style 'return' keyword is renamed 'recover' to avoid confusion with C's return
2005
Release 0.2.3 "Cornwall" (18 August) introduces the 'external' keyword, letting an Amber class wrap a Parrot PMC class directly, and moves much of the runtime out of a monolithic kernel.pir file into external PIR routines attached to the kernel classes
2005
Release 0.2.3a "Ashes" (24 September) adds a regular-expression class RULE built on Parrot's Perl 6 Rules support, a Conway's Game of Life example, an SDL-based graphical example (amber-square), and a major refactoring of the lexer and parser
2005
Release 0.3.0 "Struggle" (20 October) adds 'include' and 'load' directives for pulling in Parrot PIR and bytecode files, slurpy (variable-length) arguments, and routine bodies marked "external parrot" that are dynamically loaded from Parrot bytecode or dynclasses libraries
2005
Release 0.3.1 "Magic cookies" (15 November) reimplements the kernel classes ARRAY, BOOLEAN, CHARACTER, INTEGER, STRING and TABLE as Parrot PMCs, adds a JSON serialization class and an INTERNAL introspection class, replaces the test harness, adds around 40 new tests, and makes precondition checking opt-in via the --check option
2005
Release 0.4.0 "Induction" (20 December) implements Eiffel's 'old' keyword for postconditions, square-bracket indexing of arrays and hashes, read-only access to outer lexicals from agents, and adds an Ackermann benchmark to the examples/shootout directory
2006
Release 0.4.2 "Argument", published on 28 February, follows the Parrot 0.4.2 release ("GPW") of the same month by roughly a week and turns out to be the last Amber for Parrot release. Its notes record argument-count mismatches now being detected by Parrot and turned into exceptions, two broken examples, and estimated progress towards a 1.0 release of 47 percent on language constructs, 6 percent on libraries, 4 percent on documentation and 6 percent on robustness
2006
Browne reportedly presents the 0.4.2 release to the British Isles Eiffel user community; according to a meeting agenda posted on the Team Eiffel blog in March, he is listed as having released version 0.4.2 of "his Eiffel-inspired scripting language". No further Parrot-targeted release follows
2007
The xamber.org site is rebranded from "Amber for Parrot" to "the Amber Scripting Language" and states that Amber for Parrot was "an earlier version", with the version then being developed described as a self-contained interpreter. The project is licensed under the MIT license, with commercial licensing offered alongside
2010
The xamber.org front page still advertises the self-contained interpreter as the version under development, and the release history page still ends at 0.4.2 from February 2006. No release of the standalone interpreter appears; the site later goes offline and the domain lapses, leaving the Internet Archive as the primary record of the language

Notable Uses & Legacy

A proof of concept for Eiffel ideas on Parrot

Amber's stated purpose, in its own 0.2.0 release name and in the 2005 announcement, was to demonstrate that Eiffel concepts - contracts, class invariants, loop variants - could be carried into a dynamic scripting language running on Parrot. That was the use case, and by its author's own progress estimates it never got past roughly half of the intended language and a token library. It has no known production deployments.

Parrot interoperability demonstrations

Amber was one of the more aggressive early tests of Parrot's promise of cross-language interoperation. Its documentation shows Amber classes wrapping Parrot PMC classes, inheriting from them, extending them with new routines, calling built-in Parrot routines, writing individual Amber routines directly in PIR, calling Amber routines from PIR, and loading shared libraries - the kernel class INTEGER, for instance, was written as a wrapper around Parrot's built-in Integer PMC.

The bundled example programs

The distribution shipped a small gallery of demonstration scripts, several of which are the only surviving Amber code most people will ever see: a one-line hello world, factorial and fibonacci, a word-count clone of Unix wc, 99 bottles of beer, a number-guessing game, an interactive regular-expression explorer built on Parrot's Perl 6 Rules, Conway's Game of Life, and amber-square, a graphical example driven through Parrot's SDL wrapper.

Benchmark and test corpus

Release 0.4.0 added an Ackermann implementation to an examples/shootout directory, in the style of the Computer Language Benchmarks Game of the period, and the project accumulated a test suite that grew from 24 scripts at 0.2.0 to roughly 40 additional tests in 0.3.1 alone. No published benchmark results for Amber survive - the shootout example demonstrates that the program runs, not how fast.

Language Influence

Influenced By

Eiffel Ruby Perl 6

Running Today

Run examples using the official Docker image:

docker pull
Last updated: