Amanda
Dick Bruin's free, Miranda-like lazy functional interpreter from the Netherlands, written in ANSI C for MS-DOS and later Windows and kept alive as public-domain source.
Created by Dick Bruin
Amanda is a small, free, lazy functional language from the Netherlands: an interpreter written in ANSI C by Dick Bruin that reproduces most of David Turner’s Miranda, adds records and a few conveniences, and asks nothing in return. Bruin built it, in his own words, “for recreational purposes (solving mathematical puzzles)”, and released it with no licence whatsoever - not even a permissive one - requesting only that anyone republishing it note that no rights attach to it and that his name need not be mentioned. Its evident appeal was as a way to run the programs in Bird and Wadler’s textbook on a PC without paying for anything, and the one documented case of that - a Dutch student project - is the reason the source survives at all.
History and Origins
Miranda, first released by Research Software Ltd in 1985 and sold commercially thereafter, was the lazy functional language of the late 1980s: elegant, widely taught, and licensed. Amanda is what happens when someone wants that language on their own machine without the licence. The name looks like the tell - one letter away, and apparently the same Latin gerundive joke (“she who must be loved” against “she who must be admired”) - though Bruin nowhere explains it.
Dating Amanda precisely is difficult, and the honest answer is that nobody has published a firm origin date. The HOPL catalogue records it as designed in 1988 in the Netherlands, which matches the language’s own documentation: the manual notes that “Amanda has been designed with a PC-DOS environment in mind” and that “the awkward memory structure of PC-DOS still leaves traces in Amanda”. The earliest independent trace is the FOLDOC dictionary entry, last updated on 27 April 1998, which describes Amanda as “derived mostly from Miranda with some small changes”, written by Bruin and implemented on MS-DOS and NeXT, “available as an interperator only”. The oldest dated code that survives is the version 2 C source, most of it stamped 24 August 1998, with the Delphi interface unit for the Windows DLL dated 25 October 1998 in the listing reproduced in the manual. Everything before that is inference: the 1988 date rests on a single catalogue entry.
Two books are named in the manual as Amanda’s foundations, and both choices are visible in the result:
- Richard Bird and Philip Wadler, Introduction to Functional Programming supplies the notation. Amanda’s equational definitions, guards written
,ifand,otherwise, list comprehensions and operator names follow the book so closely that the textbook doubles as the language tutorial. - Simon Peyton Jones, The Implementation of Functional Programming Languages supplies the machinery - the graph reduction and lazy evaluation techniques that the C interpreter implements.
Design Philosophy
Amanda is deliberately modest. It is an interpreter only; there is no compiler, no separate module system beyond textual inclusion, and no runtime beyond a heap of cells whose size you set in a configuration file. The console version “acts like an advanced calculator”: you type an expression, it prints the reduced value, and appending :: prints the inferred type instead of the value.
Amanda> 1 + 2
3
Amanda> 1 + 2 ::
num
Amanda> fac 3 where fac 0 = 1; fac n = n * fac (n-1)
6
Amanda> load "test.ama"
The three restrictions the manual lists are equally candid about its scale: a global function definition may not exceed 4000 cells, the internal stack is “rather small”, and patterns may nest at most 32 levels deep. This is a teaching and puzzle-solving tool, not an industrial compiler, and it never pretends otherwise.
Where the manual is unusual is in telling you where laziness will hurt. It works through the classic prime sieve
primes = sieve [2..]
sieve (x:xs) = x : sieve [y | y <- xs; y % x ~= 0]
and explains that this version exhausts memory, because reaching x keeps a reference to the whole list (x:xs) alive; the fix is to bind x = hd l and xs = tl l in a where clause. A second worked example shows a character-frequency table blowing up under accumulated closures and repairs it with seq. Few small language manuals are that honest about their own evaluation model.
Key Features
A Miranda-shaped core. Functions are defined by equations with pattern matching and guards, with where clauses for local definitions:
mergeSort list
= list ,if half < 1
= merge (mergeSort (take half list)) (mergeSort (drop half list))
where
half = (# list) / 2
Layout matters in a particular way: in a multi-clause definition the = signs must line up exactly under each other.
A single numeric type. num covers both integers and reals; the other basic types are bool and char, with strings as lists of characters, as in Miranda.
Polymorphic type inference with algebraic types. Types are inferred, never required, and can be declared with ::. Algebraic types use ::=:
tree * ::= Leaf * | Branch (tree *) (tree *)
Records - a genuine departure from Miranda. Amanda adds record types with named fields, selector functions generated per field, partial record literals usable in patterns, and an & operator that updates a record from another:
vector * ::= { x :: *, y :: *, z :: * }
innerproduct { x=x1, y=y1, z=z1 } { x=x2, y=y2, z=z2 } = x1*x2 + y1*y2 + z1*z2
The stated drawback is that field names must be unique across all record types in scope.
Abstract types. abstype ... with hides a representation behind a signature, in the Miranda style, with the restriction that an abstract type must be a synonym of an existing type:
abstype stack *
with
push :: * -> stack * -> stack *
pop :: stack * -> stack *
top :: stack * -> *
isEmpty :: stack * -> bool
create :: stack *
stack * == [*]
A C-style preprocessor. #import textually includes another file (tracking imports so repeated or recursive includes are harmless), #synonym renames anything including operators and even keywords such as where, and #operator introduces new operators.
Pattern restrictions with a reason. Repeated variables in a left-hand side are rejected, (n+1) patterns are not supported, and overlapping clause patterns are flagged as a mistake rather than silently resolved - the manual spells out counterexamples for each.
Operators from the textbook. ~= for inequality, /\ and \/ for conjunction and disjunction, # for length, ++ and -- for list append and difference, . for composition, ! for indexing, & for record update.
Evolution
Amanda’s development happened in one visible burst and then a long quiet tail. The dated headers in the C source cluster in the late summer and autumn of 1998 - fifteen files carry 24 August 1998 - with revisions running through 1999 and into September 2000; the type checker and parser reached 2.02 in that window and the manual that ships with the sources describes V2.05. In the same period the language grew a Windows personality distinct from the portable core.
The Windows distribution consisted of ama.exe (console), amanda.exe (GUI), amadll.dll (the interpreter kernel as a DLL), amanda.ini and a help file. Two features only exist there:
- Remote objects. An Amanda script can declare
object "primes" = (state, call), defining named methods and a state-transformingcallfunction. A host program - the manual’s example is Delphi, using aTAmaObjectclass withCreate,Put,CallandGet- drives it from the outside. It is an unusually direct way of embedding a lazy infinite stream into an imperative application. - AmaGr graphics. A separate front end loads an Amanda file containing an interaction object, forwards user events to it, and paints the list of drawing commands it returns. The bundled programs include Life, Tetris, a robot simulation, spline and curve plotting and an NFA-to-DFA converter.
The only later change traceable to Bruin is a 20 March 2012 stamp on the type checker, carrying version 2.05. Two years after that, on 17 April 2014, the “completely barebones version of Amanda as provided by Dick Bruin” was pushed to GitHub - the Java GUI stripped out, an Unlicense applied, and the build reworked so make produces a working interpreter on Linux, macOS and Windows (the latter via MinGW, including cross-compilation). Students at NHL Hogeschool worked on it for three months that spring, in two teams of roughly four; the final commit is dated 18 June 2014. Their build announces itself as “Amanda V3.0” and the Debian package they left behind is versioned 3.0-1, so the version number a user sees today is the students’, not Bruin’s 2.05.
Current Relevance
Amanda today is a historical artifact that still builds. The public-domain C sources still compile with a modern toolchain - a plain make against Apple’s current clang produces a working interpreter, with two narrowing warnings and no errors - the V2.05 manual is complete and readable, and the sample programs run. What there is not is a community: the GitHub repository has single-digit stars, the last substantive commit is from mid-2014, and no distribution packages it. FOLDOC’s entry has not been touched since 1998.
Its niche - a free lazy functional language for teaching - has long since been filled. Haskell became the standard vehicle for the material Amanda was built to support, and Miranda itself, the language Amanda existed to route around, was released as open source by Turner in release 2.057, dated 8 December 2019 in its ChangeLog, which retired the original motivation altogether.
Why It Matters
Amanda is a small, clear example of a pattern that recurs throughout language history: when a good language is locked behind a licence, someone reimplements it. Amanda did not innovate, and did not try to; it took Miranda’s design, added records, wrote it in portable ANSI C, and gave it away without even asking for attribution. For students who wanted to type the programs from Bird and Wadler into a PC, that was the whole point.
It is also a useful object lesson in dating obscure software. A catalogue says 1988, a dictionary entry says 1998, and the code says August 1998 onwards - and the correct answer is to report all three rather than pick one. The language’s own manual, apologising for the memory model of PC-DOS while explaining how lazy evaluation leaks space, preserves a particular moment in the spread of functional programming better than any release note could.
Timeline
Notable Uses & Legacy
NHL Hogeschool student project (2014)
Two teams of about four students each spent three months modernising the Amanda interpreter - one team on the core, one on extra functionality - producing the portable Linux/macOS/Windows build and the Debian packaging that the public repository still carries
AmaGr graphics demonstrations
The distribution ships Amanda programs driven by the AmaGr graphical front end, including Conway's Life, Tetris, a robot simulation, spline and curve plotting, a convex hull and an NFA-to-DFA converter, all written as interaction objects that return lists of drawing commands
Delphi and Windows application embedding
amadll.dll exposes the interpreter to other Windows programs through the TAmaObject class, so a Delphi application could call named Amanda objects with Put/Call/Get and use a lazy functional kernel (the manual's example is an infinite stream of primes) as a component
Go board game in Amanda (2012)
A GitHub project implements the board game Go functionally in Amanda, one of the few third-party Amanda programs published outside the distribution itself
C#/.NET editor front end (2013)
An independent GitHub project wrapped Bruin's C interpreter as a native AmandaCore library and drove it from an AmandaInterface GUI written in C# for .NET/Mono, with a syntax-highlighting editor component - one of the few attempts to give Amanda a modern IDE