Hugs, revived.
A small Haskell interpreter in plain C, with modern base, that
builds in a minute.
Cupid is Hugs 98 brought back. Hugs is the interpreter Mark P Jones started in 1994, and the Haskell many people learned with. A group of volunteers kept it going until the September 2006 release, the last one that shipped, and in time it stopped building on current compilers. Cupid picks up from that release:
- A small interpreter in plain C. The whole interpreter is under fifty thousand lines of standard C23.
- Modern base. The libraries follow GHC's base, roughly version 4.18, so code written against today's base can load.
- Builds in a minute. You need a C compiler and a few build tools, but no Haskell compiler.
The aim behind it is one sentence: a Haskell implementation you can sit down and read. Not the fastest one, not the most complete one. The one where you can follow an expression from the characters you typed to the value that comes back, and never lose the thread. It isn't there yet: much of the C is still Hugs's, tidied rather than rewritten. But every change is judged against that aim.
Three things follow from it, and they don't change:
- It is an interpreter written in C. Source text is compiled to the code of a small abstract machine and run in the same process. No native code generation, and no linker or separate compilation step for Haskell code.
- It does not self-host. Nothing in the implementation is written in Haskell except the libraries it ships, so no Haskell compiler stands between the source you read and the program that runs.
- It stays small. The libraries are Haskell, with a few small C
helpers in
base, and grow with what programs import; the interpreter's C grows only where a library can't do the job. Anything that doesn't serve the aim gets deleted rather than parked behind a flag.
That third one does most of the work. A flag is a promise to keep two code paths working forever, and a tree full of them is exactly the tree you can't read.
A fair amount of the 2006 tree has already gone: the autoconf build, the flag-guarded extensions, the GHC/nhc98 shared library sources and the three-stage bootstrap that turned them into something Hugs could load, the platforms nobody has run this century. The design of Cupid says what stayed and why. If you want the longer story of where the code came from, that's History.
What has been added is what real code needs. The libraries have
Applicative under Monad, Semigroup under Monoid, and Foldable
and Traversable. The transformers package loads from its unmodified
source once it has been through the C preprocessor; see
Preprocess source files before loading.
Running such code doesn't mean following GHC. A feature gets in when
packages written for GHC need it, when it fits the existing type checker
and leaves code generation and the abstract machine unchanged, and when
Cupid can take the common case and reject the rest. Every extension is
always on, except overlapping instances, which need the +o option.
Unboxed types, Template Haskell and STM are out, and type families are
undecided. Language extensions lists
what is accepted and what is left out.
It builds and runs, with meson, on 64-bit Linux, macOS, Windows and the BSDs. Linux, macOS and Windows are built and tested on every change; on Windows it builds natively with MinGW-w64, see Build Cupid on Windows.
You need a C compiler, meson 1.1 or later, ninja, python3 and bison. Then:
CC=gcc meson setup build
ninja -C build
CUPIDDIR=build/libraries/cupiddir build/src/cupidCupid> map (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]
Cupid> :quit
The build never writes to the source tree. Tests are
meson test -C build, and installing is ninja -C build install.
Build Cupid on Linux and macOS
has the detail, and Build options and install
layout lists every -Dname=value you
can set.
docs/README.md is the index; the useful starting points are:
- Getting started with Cupid — a first session, from the prompt to running a program. About 15 minutes.
- Reading Cupid in a day — a map of the source and a route through it. Read this before the source.
- How Cupid runs a program — one small program, from source text to abstract machine.
- Interpreter commands and Options — what you can type at the prompt and what you can set.
- The language Cupid accepts — Haskell 2010 conformance, and the deviations and bugs that are known.
- Call C functions from Haskell — the
FFI and
cupid --ffi. - The design of Cupid — why the tree looks the way it does.
- Differences from Hugs 98 — what changed, if you used Hugs.
src/— the interpreter: lexer, parser (parser.y), type checker, bytecode compiler, evaluator, storage, and thecupidfront end, which shows the prompt, runs a program or compiles foreign-import stubslibraries/— the Haskell it ships:cupid(Cupid.Prelude, where most of the Prelude is defined, and the interpreter-specificCupid.*modules) andbase(thePreludemodule and the rest of base, with a few C helpers incbits).libraries/tools/holds the build script and the generator forCupid.SysConfigdocs/— getting started, guides, a tour of the source, reference and the manual pagetests/— the regression tests and the script that runs themdemos/— example programs, installed alongside the librarieswww/— the website, a Zola site that builds fromdocs/
Add a test for every bug you fix. Each tests/NAME.hs is a program
whose output is compared with NAME.stdout, or an interpreter session
when NAME.stdin exists; the comment at the top of tests/run-tests
describes the rest.
It's a fork of Hugs, so it needed a name in the same family. Hugs was first released on Valentine's Day, 14 February 1995, which made Cupid the obvious choice. The grin on the logo is his: Cupid has always been a bit of a troublemaker.
Cupid is written and maintained by Andrea Bedini.
It is derived from Hugs 98, which is Copyright (c) Mark P Jones,
Alastair Reid, the Yale Haskell Group, and the OGI School of Science &
Engineering at OHSU, 1994-2003, and distributed under the BSD-style
licence in License. Cupid is distributed under the same
licence. The base library carries its own BSD-style licence, in
libraries/base/LICENSE, because it is derived
from code by the GHC project, the Haskell 98 Report and the Haskell FFI
specification.
The people who built Hugs 98 are listed in Credits. The type checker and the abstract machine are their design, and the libraries grew from theirs; this tree would not exist without their work.