This page is Copyright
John N. Shutt
Here’s what you’re allowed to do with it.
Last modified: 24-Jan-21.
I’m developing a programming language called Kernel.
Kernel is a conservative, Scheme-like dialect of Lisp in which
everything is a first-class object.
“But,” you may ask, “aren’t all objects first-class in Scheme?”
(I’m glad you asked.) No, they aren’t.
Special-form combiners are second-class objects.
To borrow a phrase from the original description
of first- and second-class objects by Christopher Strachey,
they have to appear in person under their own names.
(There are also several other kinds of second-class objects in Scheme,
but special-form combiners are the most commonplace.)
The idea of first-class operative combiners,
i.e., first-class combiners whose operands are never evaluated,
has been around a long time.
Such creatures were supported by mainstream Lisps through the 1970s,
under the traditional name
but they made a mess out of the language semantics because they were
non-orthogonal to the ordinary variety of procedures constructed via
— and, more insidiously,
because at that time the mainstream Lisps were
(a language feature that causes more problems for fexprs
than it does for the less powerful
Kernel eliminates the non-orthogonality problem by breaking the classical
lambda constructor into two orthogonal parts,
one of which is the Kernel constructor for first-class operatives.
The primitive constructor of operatives is an operative called $vau,
which is almost the same as Scheme’s lambda
in both syntax and semantics,
except that the combiners it constructs are operative.
The primitive constructor of applicatives, i.e.,
combiners whose operands are evaluated, is an applicative called
wrap. wrap takes one argument,
which must be a combiner,
and constructs an applicative that passes its arguments
(the results of evaluating its operands) to the specified combiner.
The underlying combiner of any applicative can be extracted using
There is also an operative constructor of applicatives called
$lambda, that is almost exactly like Scheme’s lambda
— but Kernel’s $lambda isn’t primitive:
It can be implemented as a compound operative,
using $vau and wrap.
First-class operatives aren’t all there is to Kernel,
just the most immediately obvious difference from most Lisps.
The mandate of the Kernel language is to have a clean design,
flowing from a broad design philosophy
as refined by a series of more specific design guidelines
just one of which is that all manipulable entities should be first-class.
Some other neat (IMHO) features of Kernel are
- uniform compound definiends.
(Eliminates the motive for multiple-value returns.)
- continuation guarding.
(Exception-handling “done right”.)
- keyed dynamic variables.
(Fluid variables “done right”.)
- encapsulation types.
(Enforced abstraction meets latent typing.
Inspired by Morris’s seals.
Gives Kernel’s implementation of promises
a fundamentally different flavor than those of
- uniform handling of cyclic lists.
(Without this, cyclic lists wouldn’t be entirely first-class,
since they couldn’t be used in ways one ordinarily expects of objects,
such as testing via equal?.)
The Revised -1 Report on the Kernel Programming Language
(revised 29 October 2009)
is available in
On the theoretical side, the primary resource is my doctoral dissertation,
Fexprs as the basis of Lisp function application;
or, $vau: the ultimate abstraction,
which treats pure and impure vau-calculi.
I gave a talk at NEPLS in fall 2007
that described pure vau-calculi
not meant to be printed)).
A pure vau-caluclus is also described (even more lightly)
in Appendix C of the Kernel Report
(“De-trivializing the theory of fexprs”).
The place to look for implementations of Kernel, these days, is a blog post
The Kernel Underground,
at Manuel Simoni’s The Axis of Eval.
I did, upon a time, have my own pseudo-prototype implementation,
called SINK (“Scheme-based Interpreter for Not-quite Kernel”),
meant to be mostly compatible with unextended R5R Scheme;
it didn’t recognize some Kernel tokens, was slow as molasses…
it ran, more than I could say for the Java-based interpreter
I started years ago and never found time to finish.
I used SINK to debug library implementations for the Kernel Report,
and to play around with guarded continuations.
I suspect it’s suffered from bitrot, since.
For whatever it’s worth, here it is as a gzipped tarball:
My Academic Work page.