Hopl 4 Emacs Lisp PDF
Hopl 4 Emacs Lisp PDF
Permission to make digital or hard copies of part or all of this work for personal or classroom use is granted
without fee provided that copies are not made or distributed for profit or commercial advantage and that
copies bear this notice and the full citation on the first page. Copyrights for third-party components of this
©
work must be honored. For all other uses, contact the owner/author(s).
2018 Copyright held by the owner/author(s).
XXXX-XXXX/2018/9-ART
https://doi.org/10.1145/nnnnnnn.nnnnnnn
Contents
Abstract 1
Contents 2
1 Introduction 3
1.1 Organization 3
2 Prehistory 4
2.1 MacLisp 4
2.2 Gosling Emacs 4
3 Early history 4
4 Base language design 5
4.1 Lambda 5
4.2 Strings 6
4.3 Backquote 6
4.4 Docstrings 7
4.5 Interactive functions 7
4.6 Non-local exits 8
4.7 Buffer-local variables 9
4.8 Hooks 9
4.9 I/O 10
5 Base language Implementation 10
5.1 Byte-code interpreter 10
5.2 Tail-call optimization 11
5.2.1 Bootstrap 11
5.3 Data representation and memory management 12
5.4 Scanning the stack 12
5.5 Heap management in XEmacs 13
5.6 Squeezing the tag bits 13
5.7 New GC algorithms 14
5.8 Image dumping 15
5.9 Debugging 15
5.10 Profiling 16
5.11 JIT compilation 16
5.11.1 First attempt 17
5.11.2 Second attempt 17
5.11.3 Third attempt 17
5.11.4 Fourth attempt 17
6 XEmacs period 17
6.1 Event and keymap representations 18
6.2 Character representation 18
6.3 C FFI 19
6.4 Aliases 20
7 Emacs/XEmacs co-evolution 20
7.1 Performance improvements 20
7.2 Unicode 21
7.3 Bignums 21
7.4 Terminal-local and frame-local variables, specifiers 22
8 Post-XEmacs 23
1 INTRODUCTION
Elisp is the extension language of the Emacs text editor. In this sense, it is just a side-project
of Emacs and might be overlooked as a programming language. But Emacs itself comes with
more than a million lines of Elisp code, and yet more Elisp is distributed separately from
Emacs in various Emacs Lisp Package Archives (ELPA). If you additionally consider that
the majority of Emacs users have likely written a few lines of Elisp in their configuration
file, it is clearly one of the most widely used dialects of Lisp.
1.1 Organization
Elisp has evolved in multiple strands and implementations over the years, and thus its
evolution did not happen along a single timeline. Moreover, some aspects evolved over long
periods of time. To avoid excessive interleaving of those aspects, we have organized the
top-level structure of the paper into chronological eras. Within an era, as a new topic is
introduced, we usually follow that topic chronologically to its conclusion, even if that means
going beyond the era where it started.
We trace the overall evolution of Elisp in the software projects that implemented it. Its
predecessors EINE (1976) and Multics Emacs (1978) were themselves written in Lisp. Unix
Emacs (also known as Gosling Emacs), which appeared in 1981, was written in C but
included its own Lisp-like language. We briefly describe those predecessors in Section 2.
Emacs as we know it today itself started in early 1985. Section 3 describes the driving
motivation for the early design and implementation of Elisp. The following Section 4 traces
the evolution of the base language design, and Section 5 its implementation. Development
continued at a high pace until about 1991. Around that time, its development slowed down
and was overtaken by Lucid Emacs, later renamed XEmacs, whose development of Elisp
2 PREHISTORY
While Emacs’s original inception was as a set of macros for the TECO editor, it had no
high-level extension language. Arguably, the strongest influences for Elisp were Gosling
Emacs’s Mock Lisp and MacLisp.
2.1 MacLisp
Several early incarnations of Emacs were written in Lisp, notably on the Lisp Machine
(called EINE for “EINE Is Not EMACS”) by Dan Weinreb and in MacLisp [17, 20] by Bernie
Greenberg in 1978 [24]. The ensuing possibilities for extending the editor were attractive to
Richard Stallman, who wanted to write a new widely available version. As a result, Elisp is
a direct descendant of MacLisp.
3 EARLY HISTORY
Following Greenberg’s Multics Emacs, Richard Stallman decided to write a (for him) second
version of Emacs, which included Elisp from the start. As Greenberg’s Emacs required a
high-performance Lisp compiler to run efficiently, Richard Stallman decided to reimplement
the basis for the new Emacs in C, with an integrated Lisp interpreter. Buffer manipulation
and redisplay were written in efficient C, higher-level functionality in Lisp.
The initial design of Elisp was motivated by the pervasive requirement of extensibility;
users “must be able to redefine each character” [25]. TECO and Gosling Emacs featured
small languages that were either too obscure or too weak to support this vision. Consequently,
Richard Stallman took inspiration from MacLisp, and Elisp started as a real programming
language with powerful abstraction facilities, thus foregoing Greenspun’s tenth rule.
Moreover, Richard Stallman, made the design of Elisp embody and showcase the ideals
of Free Software. For example, not only is it legal to get and modify the source code, but
every effort is made to encourage the end-user to do so. The resulting requirements had a
profound influence on the Elisp language:
∙ The language should be accessible to a wide audience, so that as many people as possible
can adapt Emacs to their own needs, without being dependent on the availability of
someone with a technical expertise. This can be seen concretely in the inclusion in
Emacs of the Introduction to Programming in Emacs Lisp tutorial [6] targeting users
with no programming experience. This has been a strong motivation to keep Elisp on
the minimalist side and to resist incorporation of many Common Lisp features.
∙ It should be easy for the end-user to find the relevant code in order to modify Emacs’s
behavior. This has driven the development of elements such as the docstrings (see
Section 4.4) and more generally the self-documenting aspect of the language. It also
imposes constraints on the evolution of the language: the use of some facilities, such as
advice, is discouraged because it makes the code more opaque.
∙ Emacs should be easily portable to as many platforms as possible. This largely explains
why Elisp is still using a fairly naive mark&sweep garbage collector, and why its main
execution engine is a simple byte-code interpreter.
4.1 Lambda
Interestingly (unlike MacLisp), lambda was not technically part of the Elisp language until
around 1991 when it was added as a macro, early during the development of Emacs-19. In
Emacs-18, anonymous functions were written as quoted values of the form:
While the lambda macro has made this quote unnecessary for almost 30 years now, many
instances of this practice still occur in Elisp code, even though it prevents byte-compilation
of the body.
Somewhat relatedly, only in 1993 did Lucid Emacs 19.8 import the #’... reader shorthand
for (function ...) from MacLisp. Emacs followed suit the year after.
4.2 Strings
Elisp included support for string objects from the beginning of course. Originally, they were
just byte arrays.
In 1992, during the early development of Emacs-19, this basic type was extended by
Joseph Arceneaux with support for text-properties: each char of a string can be annotated
with a set of properties, which maps property names to values, where property names can
be any symbol. This can be used to carry information such as color and font to use when
displaying the various parts of the string.
XEmacs added a similar, though incompatible, feature to their strings at around the same
time, called extents. The incompatibility was due to a fundamental disagreement about how
those annotations should be propagated when strings are manipulated: XEmacs’s extents
cover a contiguous span of characters and they are objects in their own right with their own
identity, so they need to be duplicated when a substring is selected, with the new extents
not necessarily covering the same characters, and concatenation may end up bringing two
“identical” extents next to each other but cannot merge them into one. Richard Stallman felt
like this was introducing undesired complexities, and decided that Emacs’s text properties
should not have identity and should apply to the characters of the string so there is never a
question of splitting or merging text properties.
With buffer-local variables 4.7, this is one of the very rare cases where the core Elisp
language has been extended with a feature that specifically caters to the needs of the Emacs
text editor, to keep track of rendering information. It is more generally useful, of course, but
turns strings into a much fancier datatype than in most other languages.
Around 1994, support for arbitrary character sets was added to Emacs and XEmacs, which
required distinguishing between bytes and characters, and hence changing its string objects.
Characters are represented with a variable number of bytes, similar to utf-8, favoring a
compact representation at the cost of a slower random access. In practice, random access to
strings is fairly rare, so it works rather well, but it does impose a significant constraint: there
is an indirection between the string object and the bytes it holds because the aset primitive
which replaces one of the string’s characters with another by side-effect will sometimes
change the string’s length in bytes, which requires to relocate the string’s bytes elsewhere.
In XEmacs, strings have a “modified-tick” that is bumped every time a string is modified
in-place. This count can be retrieved with the new function string-modified-tick. The
XEmacs codebase never contained a user for this function, so the motivation for this is
unclear. It is a straightforward parallel to the buffer-modified-tick function that tracks
modifications to buffers, and which has many uses.
4.3 Backquote
Curiously, Elisp did not until 1994 include the reader syntax for quasiquotation common
to many other Lisps, including MacLisp [1]. Instead, the special forms that came with
quasiquotation—backquote (‘), unquote (,) and unquote-splicing (,@) were the names of
special forms. Thus, what is usually written as:
‘(a b ,c)
was written in early Elisp as:
(‘ (a b (, c)))
The releases of Emacs-19.28 and XEmacs 19.12 in 1994 finally added the proper reader
support to generate the latter version from the first. The reader had to rely on a heuristic
for that, though, because (‘a) is valid in both syntaxes but means different things: It could
either denote an old-style backquote expression (denoting the backquoted symbol a) or a
single-element list containing the new-style backquoted symbol a.
So the old format, though obsolete, was still supported, and the new format was only
recognized in some cases; more specifically the new unquote was only recognized within
a new backquote, and the new backquote was only recognized if it occurred within a new
backquote or if it did not immediately follow an open parenthesis.
Seeing how uses of old-style backquotes were not going away, in 2007 Emacs-22.2 introduced
explicit tests and warnings to bring attention to uses of old-style backquotes, while still
keeping the actual behavior unchanged.
Then in 2012 with the release of Emacs-24.1, the behavior was changed so that the old
format is only recognized if it follows a parenthesis and is followed by a space. The main
motivation for this change was the introduction of the pcase macro where patterns can also
use the backquote syntax and are commonly placed right after a parenthesis so they were
otherwise mistaken for an old-style backquote syntax.
Finally in 2018 during the development of Emacs-27 the old-style backquote syntax was
removed.
4.4 Docstrings
An important feature of Emacs from the start was the idea of self-documentation [25]. To
that end, every definition form in Elisp can include documentation in the form of a string
after the signature:
(defun ignore (&rest _ignore)
"Do nothing and return nil.
This function accepts any number of arguments, but ignores them."
nil)
This docstring can be retrieved in various ways, in particular through the user interface.
This idea was apparently first introduced in the original Emacs implementation in TECO,
then adapted to Elisp. Many languages have since adopted them, notably Common Lisp [22]
and Clojure.
4.8 Hooks
One important aspect of the extensibility Richard Stallman originally conceived for Emacs
was the ability to make existing functions run additional code without having to change
them, so as to extend their behavior. Emacs supports this at well-defined points called hooks.
A hook is simply a variable bound to a list of functions. The add-hook function adds a
function to such a list by reassigning the variable. (This makes use of the ability to refer to
a variable by its name symbol.)
Hooks are not a core language feature, but their use has been a pervasive convention in
Emacs from the start. In particular, many libraries run a hook when they are loaded to
allow customization. Also, modes generally run hooks to allow modifying their behavior.
1 set-buffer is like with-current-buffer except that it is not scoped, so it takes effect until the next
set-buffer.
The old TECO version of Emacs also allowed attaching hooks to variable changes [25], but
this feature was not provided in Elisp because Richard Stallman considered it a misfeature,
which could make it difficult to debug the code. Yet this very feature was finally added to
Elisp in 2018 in the form of variable watchers, though they are ironically mostly meant to
be used as debugging aides.
Of course, authors do not always have the foresight to place hooks where users need them,
so in 1992, the advice.el package was added to Emacs-19, providing a defadvice macro
duplicating a design available in MacLisp and Lisp Machines, that allowed attaching code
to functions even if they do not run hooks.
The way the defadvice macro gave access to function arguments did not work with
lexical scoping; furthermore time had shown that most of the non-core features of defadvice
were very seldom used, and even more seldom used properly. So in late 2012 a new package
nadvice.el was developed which provides the same core features but with a much simpler
design that tries to make better use of existing language features. It was released as part of
Emacs-24.4.
4.9 I/O
One of the ways Elisp distinguishes itself from most other programming languages is in
its treatment of input/output: rather than follow the usual design based on some kind of
file or stream object and primitives like open/read/write/close, Elisp only offers coarser
access to files via two primitive functions insert-file-contents and write-region which
transfer file contents between a file and a buffer. So all file manipulation takes place by
reading the file into a buffer, performing the desired manipulation in this buffer, and then
writing the result back into the file.
Since this approach does not extend naturally to interaction with external processes or
remote hosts, these are handled in a completely different way: there are primitive functions
that spawn a sub-process or open up a connection to a remote host and return a so-
called process object. These objects behave a bit like streams, with process-send-string
corresponding to the traditional write, but where the traditional read is replaced by
execution of a callback whenever data is received from the sub-process or the remote host.
release of Emacs-16.56 in July 1985, a byte-code function was added, which interpreted its
string argument as a sequence of stack-based byte codes to execute, along with a compiler,
written in Elisp, which translated Elisp code to that byte-code language.
While Elisp is basically a “run of the mill” programming language, just with some specific
functions tailored to the particular use of a text editor, this byte-code language is much
less standard since it includes many byte codes corresponding to Elisp primitives such as
forward-char, insert, or current-column.
While a systematic study would likely reveal that the operations that should deserve their
own byte-code in modern Elisp code are quite different, the byte-code language of Emacs
has changed very little over the years and is still essentially the same as that of 1985. The
main changes were those made to support lexical scoping; see Section 8.1.
Of course, since Emacs also comes with a simple direct Elisp interpreter, bootstrapping is
not very difficult, but several changes were needed nevertheless because the previous use of
pre-compiled files had hidden some circular dependencies. Of those, the only one that was
not removed in Emacs-21.1 is that some Elisp code relies on the fact that some functions are
autoloaded, and some of that code is used to build the file that contains all those autoload
declarations. So instead, the solution found was to keep a pre-built copy of that file in the
CVS repository.
that the GC could not be triggered at that particular point. Similarly, the relocation of
strings was a frequent source of hard-to-track bugs because only the references known to the
GC were properly updated, so the programmer had to be careful not to keep unboxed or
unregistered references to a string at any point where a GC was possible.
To try and address those concerns, for Emacs-21.1, Gerd Möllmann changed the string
compaction code and implemented a conservative stack scan. Strings are split into the string
object itself, of fixed size and non-relocatable, and the relocatable string data to which the
code (almost) never keeps a direct reference. In order to find out if a given word found
on the stack might be a potentially valid reference to a Lisp object, it keeps a memory
map that records which regions of the memory contains which kinds of Lisp objects. This
conservative stack scanning could be used either in addition to the singly linked list of
registered references, as a kind of debugging aide, or replace it altogether.
Thanks to the interactive nature of Emacs and its opportunistic GC strategy which
ensures that the GC is often run when the stack is almost empty, the slower conservative
stack scanning and the potential false positives it introduces have not been a problem. The
maintenance of the memory map, implemented as a red-black tree, was hence the main
cost of this new stack scanning, which proved competitive with the previous scheme. The
previous scheme was kept in use on some rare systems until Emacs-25.1, where all the
register/unregister code of stack references could finally be removed.
enforce that all objects be aligned on a multiple of 8, so as to free the least significant 3 bits
for use as tag bits.
At that point, there was no illusion that a maximum file size of 256MB was sufficient, but
at the same time, the design of Emacs made it basically impossible to view files larger than
4GB or edit files larger than 2GB (on a 32bit system) anyway, no matter which tagging
scheme we used, so there was not a lot of room for improvement.
In Emacs-23.2 the tagging scheme was tweaked to use 2 tags for integers, hence pushing
the maximum file size to 512MB.
To cover the remaining space, a new compilation option --with-wide-int was introduced
in Emacs-24.1 to make the boxed data use 64bit on 32bit systems. This imposes a significant
extra cost in terms of space and time but makes it possible to edit files up to about 2GB.
When this compilation option is used, tag bits are placed in the most significant bits again,
so that the 32bit of pointers can be extracted at no cost at all.
In Emacs-24.3, the allocation of objects using the Lisp Vector tag (which is used for
many more object types than just vectors) was modified: instead of calling malloc for each
such objects, they are now allocated from “vector blocs”. The motivation was not that
malloc was too slow, but that the implementation of our conservative stack scanning keeps
track of every part of the Lisp heap allocated with malloc in a balanced tree, so every
such malloc costs us an 𝑂(𝑁 log𝑁 ) operation plus a heap allocation of an extra tree node,
which was very costly for small objects both in time and space. The reason why it took
until Emacs-24.3 to fix this performance issue is that objects using the Lisp Vector tag
were historically not used in large numbers in early Elisp code. There were two factors
that changed this situation: first, over time the style of Elisp coding evolved and the use
of cl.el’s defstruct (which internally represents those objects as vectors) became much
more common, and second the closures used in the lexical-scoping feature of Emacs-24.1
also use the Lisp Vector tag.
In Emacs-24.4, the representation of objects using the Lisp Vector tag (which is used for
many more object types than just vectors) was improved so as to reduce their header from 2
words down to a single word.
In Emacs-27.1, the object representation was changed again: the distinction between
Lisp Misc and Lisp Vector was dropped by making all objects use the Lisp Vector
representation since it had been improved sufficiently to be competitive with the special-
cased Lisp Misc representation.
During 2003, Dave Love worked on replacing Emacs’s GC with Boehm’s conservative GC.
The effort went far enough to get a usable Emacs but it was never completed, mostly because
the early performance results were disappointing. It also showed that such a replacement is
non-trivial. The first issue is that various parts of Emacs’s code assume that a collection
can only occur during Elisp code execution and not during heap allocation, and of course
those assumptions are not explicitly recorded in the code. The second issue is that Emacs
currently implements various forms of ad-hoc weak references which need to be adapted to
the more standard forms of weak references.
In XEmacs, GC pauses continued to be a perceived problem. In 2005, Marcus Crestani
developed an incremental collector for XEmacs, again using a VM-based write barrier. It was
released with XEmacs 21.5.21 in 2005 [7] and soon was turned on by default. It eliminates
GC pauses from the user experience, and its asymptotic performance is competitive with
the old collector.
5.9 Debugging
Debugging support was added very early to Elisp: Emacs-16.56 already included the backtrace
debugger, which suspends execution at the time a condition is signaled, showing the current
stack backtrace and letting you examine the state of the application and place breakpoints
before pursuing execution.
For most developers, this is still the main debugger for Elisp. Of course, it has seen various
improvements over time, most of them affecting only the user interface. The main exceptions
are: in 1995 (for Emacs-19.31), it was refined so that the debugger is only invoked for some
conditions, making it possible for developers to keep this debugger enabled all the time,
without impeding normal use, and in 2012 (for Emacs-24.1) it was improved so as to be
able to execute code in the context of any activation frame, which was necessary to allow
access to lexically scoped variables.
In 1998, Daniel Laliberte developed another Elisp debugger, called Edebug. It was included
into Emacs a few years later during the early development of Emacs-19. This one works
without any special support in the interpreter; instead it instruments the Elisp source
code you want to debug, such that running this code lets you step through this code and
displays the various values returned by the evaluation of each step. We do not know from
where Daniel took this idea, but Edebug was probably the inspiration for the Portable
Scheme Debugger [14], and the same basic technique was used (and significantly improved)
in SML/NJ [31].
One interesting feature of Edebug is that in the presence of arbitrary user-defined macros,
it is generally impossible to correctly instrument source code since Edebug cannot guess
which arguments to a macro are normal Elisp expressions and which ones play a different
role. By default Edebug works around this difficulty by leaving arguments to unknown
macros non-instrumented, which is safe but suboptimal. To improve on this default behavior,
the macro author can annotate its macro with a debug specification which describes the
role of each argument using a kind of grammar formalism, so Edebug can know which parts
should be instrumented.
5.10 Profiling
In 1992, during early development of Emacs-19, Boaz Ben-Zvi implemented the profile.el
package which implemented a fairly simple Elisp profiler all in Elisp. This implementation
was based on instrumenting a set of user-specified Elisp functions by modifying their bodies
in-place to keep track of time spent in those functions.
In 1994, Barry A. Warsaw implemented the elp.el package which took a similar approach
but without modifying functions’s bodies, which was brittle and inconvenient and only
worked for functions defined in Elisp. Instead it replaced the instrumented functions with
wrappers which counted the number of calls, along with the execution time, and internally
called the original function’s definition. This package was included in Emacs-19.29 and made
profile.el obsolete. Its implementation was significantly reworked for Emacs-24.4 to make
use of the new nadvice.el package in order to add/remove instrumentation instead of doing
it in its own ad-hoc way.
Ben Wing added an Elisp profiler to XEmacs 19.14 in 1996 by instrumenting entry points
in the byte-code interpreter. In profiling mode, the byte-code interpreter provides timing
information about function calls and allocation.
In early 2011, Tomohiro Matsuyama started implementing in Emacs’s C code a sampling-
based profiler for Elisp. He finished the implementation as part of Google’s Summer of Code
of 2012, and it was included in Emacs-24.3. The main advantage of this profiler compared
to elp.el is that it does not require instrumentation, and it collects (partial) stack traces.
This means that not only the user does not need to know beforehand which functions might
be involved but it can show a an actual call tree.
5.11.1 First attempt. In 2004, Matthew Mundell developed the first JIT compiler for Elisp.
This took byte-compiled Elisp code and used the GNU Lightning library to turn it into
native machine code on the fly. The speedup obtained reached a factor of about 2 in the
best case, which was rather disappointing, so it was not included into Emacs since the extra
maintenance burden was not considered justified. An important reason for the disappointing
performance is that it only removed the immediate interpretation overhead, but did not
affect function calls nor was it able to remove redundant type checks.
5.11.2 Second attempt. Around 2012, Burton Samograd developed a second JIT compiler
for Elisp. This took a similar path, but using GNU Libjit instead. It was very simplistic,
turning each byte-code into a call to a C function. The resulting performance was not more
impressive, the author measuring a 25% speedup on a raytracer.el test.
5.11.3 Third attempt. In 2016, Nickolas Lloyd developed the third JIT compiler for Elisp,
again based on GNU Libjit and based on a similar approach. It improved on Burton’s
implementation by open-coding most common byte-codes instead, which avoided many C
function calls, but it obtained comparable results most likely because Libjit is not very good
at optimizing its code and C function calls aren’t that costly. but did get to the point of
being stable enough to be used globally.
5.11.4 Fourth attempt. In 2018, Tom Tromey took another stab at it, again using GNU
Libjit. Compared to Nickolas, it focuses exclusively on code using lexical binding, which
is likely to benefit more, and it implements additional optimizations by getting rid at
compile-time of all the manipulation of the Lisp stack. In the best case it reaches a speed up
factor of 3.5, which is not ideal, but is a bit more respectable. It can be used globally but is
still a very naive JIT compiler which requires further work to try and avoid pathological
behavior in some situations where the JIT compilation is performed too eagerly, leading to
significant slowdowns.
The discussion whether this last JIT compiler will be integrated as an experimental feature
into Emacs-27.1 is still on-going.
6 XEMACS PERIOD
In 1991, Lucid Inc., a software development company based in Menlo Park, Carlifornia,
started a project called Energize. Energize was to be a C/C++ integrated development
environment based on Emacs [10]. Lucid decided to use Emacs as the central component of
Energize. At the time, the current version of Emacs was 18, which was still essentially a
textual application. The then upcoming version of Emacs, Emacs 19, was to have a graphical
user interface and many other features that the developers at Lucid considered essential for
the development of Energize. However, at the time that Lucid needed Emacs 19, a release
was not in sight.2
While Lucid at first tried to support and thus speed up the development of Emacs 19, the
required cooperation between Lucid and the Free Software Foundation soon broke down. As
a result, Lucid forked Emacs development, creating its own Emacs variant Lucid Emacs.3
Jamie Zawinski was the primary developer of Lucid Emacs. In 1994, Lucid went bankrupt.
Sun subsequently wanted to ship Lucid Emacs with their operating system, and ended up
financing some of the continued development of Lucid Emacs, and effected a name change
to the current XEmacs.
2 The first official release of Emacs 19, Emacs 19.28, came out in on November 1, 1994.
3 The first release of Lucid Emacs came out in April, 1992.
The focus of Lucid Emacs was on providing a proper graphical user interface. As a result,
most of the changes to Elisp in Lucid Emacs / XEmacs were to support the move from a
TTY-based purely textual model to a graphical model.
representing key sequences. In strings, the high bit represented “meta,” basically restricting
Emacs to ASCII. Characters outside of strings could have more modifiers in higher bits.
This situation was no longer tenable when multi-language support came to XEmacs. The
work on MULE [19] predates widespread adoption of Unicode, and at the time XEmacs
adopted MULE (around 1994), a number of other text encodings were still in use. The
MULE character representation encoded a character as an integer that represented two
numbers, in the high and low bits respectively: One represented the national character set,
the other the associated codepoint.
To enforce the separation between characters and their associated encodings, XEmacs
20 made characters a separate data type. XEmacs had functions to convert between a
character and its numerical representation (make-char and char-int). Generally, Elisp
allows programs to mostly handle text as strings, and avoid manipulating the numerical
representation. Making characters an opaque type additionally discouraged the practice.
6.3 C FFI
As the Elisp runtime was written in C, it was always possible to add new Elisp functions
written in C to the system. Those C functions could also call Elisp functions.
However, functions written in C originally lacked the dynamic nature of Elisp, as they
had to be linked into the Emacs executable. Starting in 1998, J. Kean Johnston added
facilities to XEmacs (released with version 21.2 in 1999), which allowed Elisp code to build
and dynamically load shared libraries (called modules) written in C into running editor and
call the functions defined therein. Starting in 2002 with XEmacs 21.5.5, a number of such
modules were distributed with XEmacs, among them bindings for existing C libraries such
as Zlib, Ldap, PostgreSQL.
Even with modules in place, developers still had to create wrappers to make existing
C libraries accessible in Elisp. In 2005, Zajcev Evgeny wrote an FFI for SXEmacs [28], a
fork of XEmacs. This FFI allows loading and calling existing C libraries directly, without
intervening wrappers, by declaring the type signatures of C functions in Elisp. For example,
the FFI allows using Curl like this:
(ffi-load "libcurl.so")
(setq curl:curl_escape
(ffi-defun ’(function c-string c-string int) "curl_escape"))
(let* ((url "http://foo.org/please escape this<$!=3>")
(str (ffi-create-fo ’c-string url))
(len (ffi-create-fo ’int (length url)))
(result (ffi-call-function curl:curl_escape str len))
(ffi-get result))
Richard Stallman refused to incorporate XEmacs’s FFI into Emacs for fear that it would
open up a backdoor with which developers would be able to legally circumvent the GNU
General Public License (GPL) and thus link Emacs’s own code with code that does not
abide by these licensing terms. After many years of pressure on this issue (not just within
the Emacs project, since this affected several other GNU projects, most notably GCC), a
solution was agreed to, which was to implement an FFI that would only accept to load
libraries that came with a special symbol attesting that this library is compatible with the
GPL. As a result, after a very long wait, 2016 finally saw the release of Emacs-25.1 with an
FFI comparable in functionality to that of XEmacs. So far, we do not know of any publicly
available package which makes use of this new functionality, sadly. But rumors indicate that
it has been used in a few private projects, either to link Emacs with another language or to
extend Emacs with ad-hoc functionality implemented in C for performance reasons.
6.4 Aliases
During the development of XEmacs 19.12, which was 1995, the first official release of Emacs
19 appeared, Emacs-19.28. Emacs had implemented some XEmacs functionality, notably
the support for multiple open GUI windows. XEmacs had called these windows “screens,”
while Emacs called them “frames.” Compatibility with Emacs was an important goal for the
XEmacs developers at the time. Consequently, they renamed the associated functionality.
To preserve compatibility for Elisp code written for previous versions of XEmacs, XEmacs
introduced forms define-obsolete-functional-alias and define-obsolete-variable-alias.
The byte-code compiler would emit warnings if these aliases were used, but still compile the
code.
Emacs had long had a defalias form to declare function aliases, on which the define-
obsolete-function-alias functionality could be based.4 XEmacs 19.12 added a corre-
sponding primitive form for variable aliases, defvaralias, and functions variable-alias
and indirect-variable to examine the alias chains.
These additions were only merged into Emacs-22.1 in 2007.
7 EMACS/XEMACS CO-EVOLUTION
Some aspects of Elisp evolved in both Emacs and XEmacs, with both versions borrowing
design and code from the other.
4 Curiously, defalias was elided from the Emacs code base in 1986 and reintroduced in 1993.
were made to the byte-code format in XEmacs, the two instruction sets eventually drifted
and became incompatible.
In late 2009, the Emacs byte-code interpreter was modified by Tom Tromey to implement
token threading using GCC’s computed goto feature, when available. A patch for this feature
had been submitted in May 2004 by Jaeyoun Chung, but the speed improvement was not
even measured at that time so it had not raised much enthusiasm. Tom’s implementation
was no better, and the speed up was a meager 5% but he pushed stronger for its inclusion,
which only happened with Emacs-24.3, in 2012. The reason why the speed improvement is a
bit disappointing was not really investigated, but the general consensus is that the byte-code
interpreter is simply not very optimized, so the relative cost of the switch is not as high as
it could (or, arguably, should) be.
7.2 Unicode
As Unicode [29] became universally adopted, Emacs and XEmacs both supported the
standard. Emacs 21.1 supported the utf-8 coding-system, but it was not unified with other
charsets. In 2001, Emacs 22.1 introduced a form of unification between the Unicode charset
and several other charsets. XEmacs did the same in 2001 with release 21.4.
As Unicode evolved a universal text representation and supplanted many of the earlier
encodings,, Emacs and XEmacs both started efforts to replace the internal MULE represen-
tation by Unicode altogether. This appeared in Emacs 23 (2007) and XEmacs 21.5 (starting
about 2010 in a separate branch). As a result, the integer representation of a character in
both Emacs and XEmacs is its Unicode scalar value.
7.3 Bignums
Somewhat surprisingly for Lisp, Elisp had no support for arbitrarily large integers (bignums)
for many years. Integer range was restricted by word size on the underlying machine, and
representation changes over time have affected the exact range available in Elisp. As a
result, various functions dealing with numbers beyond the fixnum range had to implement
workarounds. Notable are file-attributes (which may use a pair of two fixnums for inode
numbers, device numbers, user id, and group id, and may use a float for the file size) and
current-time, which returns a list of numbers to encode the time. Another place where
the limited range of integers has caused friction has been in the fact that it also limits the
maximum size of file that can be edited.
Moreover, Elisp was used for more and more applications beyond text editing, and also
had to implement workarounds. As a result, Calc, an advanced calculator and computer
algebra tool, which has shipped with the Emacs distribution since 2001, had to implement
bignum arithmetic in Lisp.
Jerry James added bignums to XEmacs 21.5.18 in 2004, using the GMP library [11].
In Emacs, Gerd Möllman started work on adding support for bignums via GMP around
October 2001, but never finished it. It’s only in August 2018 that Tom Tromey, with the help
of Paul Eggert and several other developers, finally added support for bignums to Emacs
(again, using GMP).
The support for bignums in XEmacs includes arbitrary-precision integers, rationals, and
floating point numbers and is optional at build time, so while it is fairly complete, XEmacs’s
Elisp programs still cannot rely on bignum support. Consequently, file-attributes,
current-time, and Calc still do not take advantage of bignums.
In contrast, Emacs’s bignum support is currently restricted to arbitrary-precision integers
but the feature is provided unconditionally by bundling the mini-gmp library with Emacs for
those systems where GMP is not installed. The lack of support for rationals and arbitrary-
precision floats is only a reflection of the lack of interest for these features. The support was
made unconditional so that the code does not need to keep alternate code paths for when
bignums are not available. As a result, file-attributes and Calc have been modified to
use native bignums (but not current-time which would need arbitrary-precision rationals
or floating points).
The introduction of bignums raises some design issues in Elisp, as previously integers
were always unboxed. This meant that the fast eq only behaved differently from eql on
floating point numbers. As a result, Elisp could assume that, if two integers represented the
same number, eq would return true on them. Bignums are heap-allocated, so the same is
not necessarily true for two bignums. In XEmacs, eq can return nil in this case, and this
seems to have caused no serious problems. This issue is still being discussed with Emacs.
and when it is left), and at that occasion it was decided that variables should not be allowed
to be both buffer-local and frame-local.
The work on those bugs made it clear that the implementation of buffer-local and frame-
local bindings was too hard to follow, so in 2010 the implementation was reworked to
make the different possible states more explicit in the code, and at the same time it was
decided that frame-local variables should be deprecated: while buffer-local variables are used
extensively in Elisp and replacing them with explicit accesses to fields or properties of buffer
objects would make Elisp code heavier, frame-local variables were not in widespread use and
could easily be replaced by more traditional use of accessors to frame properties, making it
hard to justify the extra complexity in the implementation. So in 2012 with the release of
Emacs-24.1, it became impossible to let-bind frame-local variables any more, and in 2018
with the release of Emacs-26.1 frame-local variables have been removed altogether.
8 POST-XEMACS
Between 1991 and 2001, Emacs improved rather slowly compared to XEmacs. But starting
around 2001, Emacs’s pace picked up again. In 2008 Richard Stallman stepped down (again)
from the maintainership of Emacs, and the new maintainers have proved more eager to
make Elisp evolve, whereas XEmacs started to lose momentum starting about 2010.
This section discusses some notable evolution of the design of Elisp during that time:
lexical scoping (Section 8.1), Common Lisp compatibility (Section 8.4), generalized variables
(Section 8.5), object-oriented programming (Section 8.6), native support for objects (Sec-
tion 8.7), generators (Section 8.8), concurrency (Section 8.9), inline functions (Section 8.10)
and various attempts at module systems (Section 8.11).
introduced the curry operator apply-partially to cover similar use cases without
those drawbacks.
∙ The global visibility of variable names, requiring more care with the choice of local
names. The convention followed in Emacs to name all global variables with a package-
specific prefix works well to avoid name conflicts, except in the presence of higher-order
functions, like reduce, and it was also problematic in a few other cases such as in the
byte-compiler: in order to emit warnings about the use of undeclared variables, the
byte-compiler just tested whether that variable was already known to Emacs, which
always returned true for those variables locally bound by one of the functions on the
call stack, such as the functions in the byte-compiler itself. So some code was made
uglier with long local variable names in order not to interfere with other local bindings.
Worse: these “solutions” were never really complete.
The only fully satisfactory solution to the desire for lexical scoping in Elisp was that it
should be the scoping used by default by all binding constructs, as is the case in Common
Lisp. But at the same time, there was a non-negotiable need to preserve compatibility with
existing Elisp code, although some limited breakage for rare situations could be tolerated.
The vast majority of existing Elisp code was (and still is) agnostic to the kind of scoping
used in the sense that either dynamic or lexical scoping gives the same result in almost
all circumstances. This was true of early Elisp code and has become even more true over
time as the byte-compiler started to warn about references to undeclared variables. Warning
about unused variables would have probably pushed even more Elisp code to be agnostic.
But in any case, it seemed clear that despite the above, the majority of Elisp packages relied
somewhere on dynamic scoping. So while there was hope to be able to switch Elisp to use
lexical scoping, it was not clear how to find the few places where dynamic scoping is needed
so as to avoid breaking too many existing packages.
In 2001, Matthias Neubauer implemented a code analysis that, instead of trying to find
the places where dynamic scoping is needed, tries to find those bindings for which lexical
scoping would not change the resulting semantics [18]. This tool could have been used to
mechanically convert Elisp packages to a lexically scoped version of Elisp, while preserving
the semantics. The plan with this approach was to facilitate moving Elisp code to Scheme
eventually, but the overall project was too large to ever be realized.
Around 2002, Miles Bader started working on a branch of Emacs with support for lexical
scoping. His approach to the problem was to have two languages: an Elisp with dynamic
scope and another with lexical scope. Each file was tagged to indicate which language was
to be used, and in turn, each function was tagged with which language it was using, so
functions using dynamic scope could seamlessly call functions with lexical scope and vice
versa. This way, old code would keep working exactly as before and any new code that
wanted to benefit from lexical scoping would simply have to add the corresponding “-*-
lexical-binding:t -*-” at the beginning of the file.
The two languages were sufficiently similar that the new lexically scoped variant only
required minor changes to the existing interpreter. But the changes needed to support this
new language in the byte-compiler were more problematic, causing progress on this branch
to be slow. This branch was kept up-to-date with the main Emacs development but the
work was never completed.
It was only in 2010 that Stefan Monnier’s student Igor Kuzmin worked on a summer project
in which he tried to solve the problem differently: instead of directly adding support for
lexical scoping and closures to the single-pass byte-compiler code (which required significant
changes to the code), the idea was to implement a separate pass to perform closure conversion
as a pre-processing step. This freed the closure conversion from the constraints imposed by
the design of the single-pass byte-compiler, making it much easier to implement, and it also
significantly reduced the amount of changes needed in the byte-compiler, thus reducing the
risk of introducing regressions.
Two years later, Emacs-24.1 was released with support for lexical scoping based on Miles
Bader’s lexbind branch combined with Igor’s closure conversion. The main focus at that
point was to:
∙ minimize the changes to the existing code to limit incompatibility with existing Elisp
packages;
∙ make sure performance of existing code was not affected by the new feature;
∙ provide reliable support for the new lexical scoping mode, though not necessarily with
the best performance.
Changes to the byte code were introduced as part of the lexical scoping feature that appeared
in 2012 in Emacs-24.1, but were actually developed much earlier, probably around 2003.
Until the introduction of lexical-scoping, the stack-based byte-code only used its stack in
the most simple way, and did not include any stack operation beyond pop/dup/exch, so to
better support lexical scoping where the lexical variables are stored on the stack, several
byte-codes were added to index directly into the stack, to modify stack slots, and to discard
several stack elements at once.
Performance of the new lexical scoping mode proved to be competitive with the performance
of the dynamic scoping mode except for its interaction with the catch, condition-case,
and unwind-protect primitives whose underlying byte-codes were a poor fit, requiring
run-time construction of Elisp code to propagate the lexical context into the body of those
constructs. So in Emacs-24.4, new byte codes were introduced and the byte-code compiler
was modified to be able to make use of them. Nowadays, code compiled using lexical scoping
is generally expected to be marginally faster than if compiled with dynamic scoping.
macro-expansion and continues with the non-expanded code as in the past, though not
without duly notifying the user about the problem.
Emacs-25.1 additionally fine-tuned these macro-expansion phases (both while loading
a file and while compiling them) according to the section 3.2.3.1 of the Common Lisp
HyperSpec [22], so as to improve the handling of macros that expand to both definitions
and uses of those definitions.
8.3 Pcase
While working on the lexical-binding feature, Stefan Monnier grew increasingly frustrated
with the shape of the code used to traverse the abstract syntax tree, littered with car, cdr
carrying too little information, compared to the kind of code he would write for that in
statically typed functional languages with algebraic datatypes.
So he started working on a pattern matching construct inspired by those languages. Before
embarking on this project, he looked for existing libraries providing this kind of functionality,
finding many of them for Common Lisp and Scheme, but none of them satisfying his
expectations: either the generated code was not considered efficient enough, or the code
seemed too difficult to port to Elisp, or the set of accepted patterns was too limited and not
easily extensible.
So the pcase.el package was born, being first released as part of Emacs-24.1, and used
extensively in the part of the byte-code compiler providing support for lexical binding.
Additionally to the pcase macro itself that provides a superset of Common Lisp’s case
macro, this package also provides the pcase-let macro, which uses the same machinery
and supports the same patterns in order to deconstruct objects, but where it is allowed
to assume that the pattern does match and hence can skip all the tests, leaving only the
operations that extract data.
After the release of Emacs-24.1, Stefan was made aware of Racket’s match construct [8],
which somehow eluded his earlier search for existing pattern matching macros and whose
design makes it easy to define new patterns. The implementation of Racket’s match could
not be easily reused in Elisp because it relies too much on the compiler’s efficient handling
of locally defined functions, but pcase.el was improved to follow some of the design of
Racket’s match. The new version appeared in Emacs-25.1 and the main resulting novelty
was the introduction of pcase-defmacro which can define new patterns in a modular way,
often using the new low-level pattern app.
8.4 CL-lib
While the core of Elisp has evolved very slowly over the years, the evolution of other Lisps
(mostly Scheme and Common Lisp) has put pressure to try and add various extensions
to the language. As it turns out, Elisp, to a first approximation, can be seen as a subset
of Common Lisp, so already in 1986 Cesar Quiroz wrote a cl.el package which provided
various Common Lisp facilities implemented as macros.
Richard Stallman never wanted Elisp to morph into Common Lisp, but he saw the value
of offering such facilities, so this cl.el package was included fairly early on into Emacs, and
has been one of the most popular packages, used by a large proportion of Elisp packages.
Yet, Richard did not want to impose cl.el onto any Emacs user, so he imposed a policy
where the use of cl.el was restricted within Emacs itself. More specifically, Elisp packages
bundled with Emacs were restricted to limit their use of cl.el in such a way that cl.el
never needed to be loaded during a normal editing session. Concretely, this meant that the
only features of cl.el that could be used were: macros and inlined functions.
The reasons why Richard did not want to use cl.el and turn Elisp into Common Lisp are
not completely clear, but the following elements seem to have been part of the motivation:
(1) Common Lisp was considered a very large language back then, so in all likelihood it
would have taken a significant effort to really make Elisp into a reasonably complete
implementation of Common Lisp.
(2) Many aspects of Common Lisp design did not make consensus, as evidenced by the
divide between Common Lisp and of Scheme. Richard disliked several aspects of
Common Lisp’s design, such as the use of keyword arguments, especially in low-level
primitives like mapcar.
(3) Some aspects of Common Lisp’s design can incur an important efficiency cost, and
Emacs already carried the stigma of eight megabytes and constantly swapping, so there
were good reasons to try and not make Elisp’s efficiency any worse.
(4) Keeping Elisp small meant that users could participate in its development without
having to learn all of Common Lisp. When inclusion of Common Lisp features was
discussed, Richard would often point out the cost in terms of the need for more, and
more complex, documentation.
(5) The implementation of cl.el was fairly invasive, redefining some core Elisp functions.
(6) Finally, turning Elisp into Common Lisp would imply a loss of control, in that Emacs
would be somewhat bound to Common Lisp’s evolution and would have to follow the
decisions of the designers of Common Lisp on most aspects.
Over the years, the importance of the first two points has waned to some extent. Also the
popularity of the cl.el package, as well as the relentless pressure from Emacs contributors
asking for more Common Lisp features has also reduced the relevance of the third point.
XEmacs took the easy route on this and loaded cl.el into the standard XEmacs image,
starting with XEmacs 19.14 in 1996. Emacs instead took a longer road, where over the years,
various macros and functions from cl.el were found to be sufficiently popular to move
them into Elisp proper:
1997 The release of Emacs-20.1 sees the move of the macros when and unless as well as
the functions caar, cadr, cdar, and cddr.
2001 Emacs-21.1 includes the hash-table functions, reimplemented in C, as well as the
Common Lisp concept of keywords (though only as objects, not as arguments). Addi-
tionally the macros dolist, dotimes, push, and pop are also added to Elisp, which
introduced some difficulties: in cl.el those macros included extra functionality which
relied on parts of cl.el which we did not want to move to Elisp proper, specifically
block/return and generalized references. For that reason the macros added to Elisp
do not actually replace those of cl.el; instead when cl.el is loaded, it overrides the
original macros with its own version.
2007 Emacs-22.1 adds delete-dups, which provides a subset of cl.el’s delete-duplicates.
2012 Emacs-24.1 adds macroexpand-all and lexical scoping, which obsoletes cl.el’s
lexical-let.
2013 Emacs-24.3 adds compiler macros, setf and generalized references.
2018 To the cXXr functions incorporated in Emacs-20.1, Emacs-26.1 adds the remaining
cXXXr functions. The resistance against those was mostly one of style, since they tend
to lead to poorly readable code.
During the development of Emacs-24.3 the issue of better integration of the cl.el package
came up again. The main point of pressure was the desire to use cl.el functions within
packages bundled with Emacs. The main resistance from Richard Stallman was coming from
the last point above, but this time, a compromise was found: replace the cl.el package with
a new package (called cl-lib.el) which provides the same facilities but with names which
all use the cl- prefix. This way, the cl-lib.el package doesn’t turn Elisp into the Common
Lisp language, but instead provides Common Lisp facilities under its own namespace, leaving
Elisp free to evolve in its own way.
On that occasion, some details of cl.el’s implementation were reworked to be less invasive.
The main aspect was that cl.el redefined Emacs’s macro-expansion wholesale with its own
implementation, which incorporated support for lexical-let, flet, and symbol-macrolet,
so this was reworked in cl-lib.el to provide those features while still using the standard
macro-expansion code.
To encourage adoption of this new library, a forward compatibility version of cl-lib.el
for use on older Emacs and XEmacs versions was released at the same time as Emacs-24.3.
Despite the annoyance of having to use a cl- prefix, which caused some resistance to this
new library, the change has been surprisingly successful if we look at the proportion of new
packages which use cl-lib.el instead of cl.el.
popular), but there was also a desire to improve the defmethod with support for :around
methods and dispatch on other types than those defined with defclass.
It became quickly evident that the implementation of method dispatch needed a complete
overhaul: rather than constructing combined methods up-front and memoizing the result, as
in typical CLOS implementations, EIEIO’s dispatch and call-next-method did all their
work dynamically, relying on dynamically-scoped variables to preserve state in a way that
was both brittle and somewhat inefficient.
So, instead of improving EIEIO’s defmethod, a completely new version of CLOS’s
defmethod was implemented in the new cl-generic.el package, which appeared in Emacs-
25.1. The main immediate downside was that the idea to cleanup the rest of EIEIO (which
implements defclass objects) ended up forgotten along the way. The implementation is
not super efficient, but it’s already several times faster than the previous one in EIEIO.
This package provides largely the same featureset as CLOS’s defmethod, except for some
important differences:
(1) Method combinations cannot be specified per method like in CLOS, but instead new
method combinations can be added globally by adding appropriate methods to cl-
generic-combine-methods. This seemed like a good idea, but there is no known user
of this feature at this time, not even internal.
(2) The set of supported specializers is not hard-coded. Instead, they can be defined in
a modular way via the notion of generalizer inspired from [23]. This is used both
internally (to define all the standard specializers) as well as in some external packages,
most notably in EIEIO to support dispatching on defclass types.
The main motivation for the first difference above was that CLOS’s support for method
combinations seemed too complex: the cost of implementation was not justified by the
expected use of the feature, so it was replaced by a much simpler mechanism.
As for the second difference, it was made necessary by the need to dispatch on EIEIO
objects even though cl-generic.el could not depend on EIEIO since it was not clean
enough. There were additional motivations for it, though: not only it was clearly desirable
to be able to define new specializers, but it also made the implementation of the main
specializers cleaner, and most importantly it seemed like an interesting problem to solve.
Existing code that could make use of this new machinery, required dispatching on contex-
tual information (i.e. on the current state) rather than only on arguments. Consequently,
cl-generic.el also adds support to its cl-defmethod for pseudo-arguments of the form
“&context (EXP SPECIALIZER )”. This is used for methods which are only applicable in
specific contexts, such as in specific major modes or in frames using a particular kind of
GUI.
The implementation of cl-generic.el was accompanied by an extension of the on-line
help system so as to be able to give information not just about Elisp variables, functions, and
faces but also other kinds of named elements, starting with types. And to go along with that,
the implementation of cl-defstruct was improved to better preserve information about
the type hierarchy so that the on-line help system can be used to traverse it. This started as
an attempt to adapt to cl-generic.el the EIEIO facilities to explore interactively EIEIO
objects and methods, but is more modular and better integrated with the rest of Emacs’s
on-line help system.
8.8 Generators
With the success of Python’s and Javascript’s iterators and generators, some Emacs users
felt like Elisp was lacking in abstraction, so in 2015, Daniel Colascione developed the
generator.el, which was included into Emacs-25.1. It makes it easy and convenient to
write generators using macros iter-lambda and iter-yield. Its implementation is based
on a kind of local conversion to continuation-passing style (CPS) and hence relies extensively
on the use of lexical scoping, to work around the fact that Elisp does not directly provide
something like call/cc to access underlying continuations. It only handles a (relatively
large) subset of Elisp, because CPS conversion of forms like unwind-protect cannot be
defined in general in Elisp.
8.9 Concurrency
Elisp is a fundamentally sequential language, and it relies very heavily on side-effects to
a global state. Yet, its use in an interactive program has inevitably lead to a desire for
concurrency to try and improve responsiveness. So concurrency appeared very early on:
already in Emacs-16.56 Emacs included support for asynchronous processes, i.e. the execution
of separate programs whose output was processed by so-called process filters whenever the
Elisp execution engine is idly waiting for the next user command.
While this very limited form of cooperative concurrency was slightly improved in 1994’s
Lucid Emacs 19.9 and 1996’s Emacs-19.31 by adding native support for timers (they were
earlier implemented as an asynchronous process sending Emacs output at the requested
time), it has been the only form of concurrency available for most of Emacs’s life.
Adding true shared-memory concurrency to Elisp is very problematic because of the
pervasive reliance on a shared state in all of existing Elisp code, but the limited existing
support was limiting even in those cases where it was technically sufficient: many Elisp
packages which interact with external programs block many more times than really necessary,
simply because in order to avoid it the coder needs to write their code in a continuation-
passing style, which interacts poorly with dynamic scoping and requires significant surgery
to retro-fit to existing code.
So, shared-memory concurrency was largely considered as inapplicable to Elisp. Neverthe-
less, in November 2008, Giuseppe Scrivano posted a first naive attempt at adding threads to
Elisp. This effort did not go much further, but it inspired Tom Tromey to try its own luck
at the game. In 2010, he started to work on adding shared-memory cooperative concurrency
9 ALTERNATIVE IMPLEMENTATIONS
Implementation of Elisp have not been confined to Emacs and its derivatives. Two implementations—
Edwin and JEmacs—are notable for running Elispcode on editors implemented independently
from Emacs. Moreover, a Common Lisp package emulates Emacs Lisp, and Guile Scheme
also comes with support for the Elisp language. These implementations all aim at running
existing Elisp code in alternative environments, and consequently feature no significant
language changes.
9.1 Edwin
Edwin is the editor that ships with MIT Scheme [16]. Its user interface is based on that of
Emacs. Edwin is implemented completely in Scheme, and Scheme is its native extension
language. Additionally, Matthew Birkholz implemented an Elisp interpreter in Scheme that
was able to run substantial Elisp packages [3] at the time, among them the Gnus news
reader.
9.2 Librep
In 1993, John Harper started working on an embeddable implementation of Elisp called
Librep, which is most famously used as the extension language of the Sawfish window
manager. While Librep started as a Lisp dialect that was mostly compatible with Elisp,
it has since significantly diverged, including a module system, lexical scoping, tail-call
elimination, and first-class continuations.
9.4 JEmacs
JEmacs [5] is an editor that ships with Kawa Scheme [4]. JEmacs comes with support for
running some Elisp code. Its implementation (written partly in Java and partly in Scheme)
works by translating Elisp code to Scheme, and running the result.
9.5 Guile
Guile Scheme [9] was conceived as the universal extension language of the GNU project, with
the specific intention of replacing Elispin Emacs at one point. This has not happened (yet),
but Guile does ship with a fairly complete implementation of Elisp that translates Elisp
programs to Guile’s intermediate language. It is used in the Guile-Emacs system, which is a
work-in-progress modification of Emacs where the Elisp engine is provided by Guile.
9.6 Emacs-Ejit
In 2013, Nic Ferrier implemented in Elisp a compiler from Elisp to Javascript. This was
designed so as to be able to write complete web sites all in Elisp, using the Elnode Elisp
package to do the server-side processing and using Emacs-Ejit to write the client-side code in
Elisp as well. It does not really aim to run any Elisp package in your browser, so its runtime
library only provides a small subset of Elisp’s standard primitives.
10 CONCLUSION
Many steps of Elisp’s evolution have been the result of efforts of single individuals, driven by
a specific purpose, yet it has managed to keep an arguably sane and cohesive overall design,
thanks on the one hand to a maintainership which was more interested in improving the
text editor than the language and kept an eye on the longer term, and on the other to the
10.1 Acknowledgments
Emacs and Elisp are the result of the contribution of an impressive number of individuals.
We thank them all for their contributions of course. With respect to this article, while
the efforts put into maintaining the revision history of Emacs through the various revision
systems it has used have been very helpful, we’d like to thank also Lars Brinkhoff for his
archiving work at https://github.com/larsbrinkhoff/emacs-history which fills some of the
holes of the early life of Emacs.
REFERENCES
[1] A. Bawden. Quasiquotation in Lisp. In O. Danvy, editor, Proceedings of the ACM SIGPLAN Workshop
on PEPM Partial Evaluation and Semantics-Based Program Manipulation PEPM ’99, pages 4–12, San
Antonio, Texas, USA, Jan. 1999.
[2] B. Berliner. Cvs ii: Parallelizing software development. In USENIX Winter 1990 Technical Conference,
volume 341, page 352, 1990.
[3] M. Birkholz. Emacs Lisp in Edwin Scheme. Technical Report A.I. Memo No. TR-1451, Massachusetts
Institute of Technology, Sept. 1993.
[4] P. Bothner. The Kawa Scheme language, 1999. URL https://www.gnu.org/software/kawa/index.html.
[5] P. Bothner. JEmacs - the Java/Scheme-based Emacs. Free Software Magazine, Mar. 2002. URL
http://jemacs.sourceforge.net/JEmacs-FSM.html.
[6] R. J. Chassell. An Introduction to Programming in Emacs Lisp. Free Software Foundation, Boston,
Massachusetts, Emacs version 26.1 edition, 2018. URL https://www.gnu.org/software/emacs/manual/
html mono/eintr.html.
[7] M. Crestani. A new garbage collector for XEmacs. Master’s thesis, Universität Tübingen, 2005. URL
http://crestani.de/xemacs/pdf/thesis-newgc.pdf.
[8] M. Flatt and PLT. The Racket Reference. PLT, v.7.0 edition, Aug. 2018. URL https://docs.racket-lang.
org/reference/index.html.
[9] Guile Reference Manual. Free Software Foundation, July 2018. URL https://www.gnu.org/software/
guile/manual/.
[10] R. P. Gabriel. Letter to Chris DiBona and Tim O’Reilly. Web page. URL https://www.dreamsongs.
com/DiBona-OReillyLetter.html.
[11] GMP. The GNU Multiple Precision arithmetic library. URL https://gmplib.org/.
[12] J. Gosling. Unix Emacs. Carnegie-Mellon University, 1981.
[13] B. Hayes. Ephemerons: A new finalization mechanism. In Proceedings of the 12th ACM SIGPLAN
Conference on Object-oriented Programming, Systems, Languages, and Applications, OOPSLA ’97,
pages 176–183, New York, NY, USA, 1997. ACM.
[14] P. Kellomäki. PSD - a portable scheme debugger. SIGPLAN Lisp Pointers, VI(1):15–23, jan 1993.
URL https://dl.acm.org/citation.cfm?doid=173770.173772.
[15] B. Lewis, D. LaLiberte, R. Stallman, and GNU Manual Group. GNU Emacs Lisp Reference Manual.
Free Software Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 3.1 edition, 2018. URL
https://www.gnu.org/software/emacs/manual/elisp.html.
[16] MIT/GNU Scheme. Massachusetts Institute of Technology, 2014. URL https://www.gnu.org/software/
mit-scheme/documentation/mit-scheme-user/index.html. Version 9.4.
[17] D. A. Moon. MACLISP Reference Manual. Project MAC - M.I.T., Cambridge, Massachusetts, Apr.
1974. Revision 0.
[18] M. Neubauer and M. Sperber. Down with Emacs Lisp: Dynamic scope analysis. In International
Conference on Functional Programming, pages 38–49, Florence, Italy, Sept. 2001. URL http://www.
deinprogramm.de/sperber/papers/dynamic-scope-analysis.pdf.
[19] K. Ohmaki. Open source software research activities in AIST towards secure open systems. In 7th
IEEE International Symposium on High Assurance Systems Engineering, pages 37–41, Oct 2002.
[20] K. M. Pitman. The revised Maclisp manual. Technical Report Technical Report 295, Laboratory for
Computer Science, MIT, Cambridge, Massachusetts, 1983. URL http://www.maclisp.info/pitmanual/.
Saturday Morning Edition.
[21] K. M. Pitman. Condition handling in the Lisp language family. In A. Romanovsky, C. Dony,
J. Knudsen, and A. Tripathi, editors, Advances in Exception Handling Techniques, volume 2022
of Lecture Notes in Computer Science. Springer, 2001. URL http://www.nhplace.com/kent/Papers/
Condition-Handling-2001.html.
[22] K. M. Pitman. Common Lisp HyperSpec, 2005. URL http://www.lispworks.com/documentation/
HyperSpec/Front/index.htm.
[23] C. Rhodes, J. Moringen, and D. Lichteblau. Generalizers: New metaobjects for generalized dispatch. In
European Lisp Symposium, 2014. URL http://research.gold.ac.uk/9924/1/els-specializers.pdf.
[24] R. Stallman. My Lisp experiences and the development of GNU Emacs. Speech transcript, Oct. 2002.
URL https://www.gnu.org/gnu/rms-lisp.en.html. International Lisp Conference.
[25] R. M. Stallman. EMACS: The extensible, customizable self-documenting display editor. In Proceedings
of the ACM SIGPLAN SIGOA Symposium on Text Manipulation, pages 147–156, New York, NY, USA,
1981. ACM. URL https://www.gnu.org/software/emacs/emacs-paper.html.
[26] G. L. Steele, Jr. and R. P. Gabriel. The evolution of Lisp. In The Second ACM SIGPLAN Conference
on History of Programming Languages, HOPL-II, pages 231–270, New York, NY, USA, 1993. ACM.
[27] S. Steingold. Load Emacs-Lisp files into Common Lisp, 1999. URL https://sourceforge.net/p/clocc/hg/
ci/default/tree/src/cllib/elisp.lisp.
[28] SXEmacs. SXEmacs – redefining Emacs. Web site. URL http://www.sxemacs.org/.
[29] The Unicode Consortium. The Unicode Standard. Technical Report Version 6.0.0, Unicode Consortium,
Mountain View, CA, 2011. URL http://www.unicode.org/versions/Unicode6.0.0/.
[30] W. F. Tichy. RCS - a system for version control. Software Practice&Experience, 15(7), 1985. URL
https://www.gnu.org/software/rcs/tichy-paper.pdf. Purdue University.
[31] A. Tolmach and A. W. Appel. Debugging Standard ML without reverse engineering. In Conference on
Lisp and Functional Programming, pages 1–12, 1990. URL https://dl.acm.org/citation.cfm?id=91564.
[32] B. Wing, B. Lewis, D. LaLiberte, and R. Stallman. XEmacs Lisp Reference Manual, version 3.3 edition,
1998. For XEmacs Version 21.0.