Emacs Lisp really isn't bad

Emacs Lisp really isn't bad   emacs programming

It seems to be common wisdom in the Emacs community that Emacs Lisp is just this terrible programming language that everyone hates, on the level with something like PHP. However, I just don't think that's the case. Emacs Lisp is strongly typed, has an extremely complete and extensive standard library, it has all of the power of a full Lisp including live mutation of a running system, procedural macros, symbolic programming, and homoiconicity, a copy of the Common Lisp standard library (including Common Lisp generic multimethods), and an implementation of Common Lisp's incredibly powerful object system. Yes it's a Lisp-2, but so is Common Lisp, and I personally think that's an inconvenience, but not a fatal flaw.

Honestly, in my opinion, it has both fewer gotchas and is simpler, more straightforward, and more pleasant to program in than many popular modern languages, including things like JavaScript and Python. Python is chockablock full of shittily implemented (the fact that match ing on a variable can assign it, even if it already has a value, or that default argument values are effectively singletons, or any number of other things) and non-composable features (lambdas can't have multiple lines, anyone?). Or what about the insanity that is JavaScript? Even Lua has its share of truly awful design decisions (such as the fact that "arrays" are just tables with consecutive integer keys, so if you accidentally introduce a non-consecutive key into the middle of the consecutive keys of an "array", the iteration primitives will assume that's the end of the array and stop there).

Moreover, Emacs Lisp has a lot of things built into it that make it much more suited to something like Emacs than the alternatives:

  • Things it gets from being a Lisp that other languages lack:

    Macros
    Macros are an extremely important facility to have for something like Emacs, where you want a powerful, general facility for package authors to create domain-specific languages for less-technical users to create the functionality they want (see things like Transient or Easy Menu or Use Package or Hydra, or the macros provided in the Emacs Lisp standard library like with-eval-after-load or with-current-buffer or with-temp-buffer), and want a language that aligns with the Emacs philosophy of being flexible and extensible and programmable itself. Yes, you can do some degree of domain-specific-language creation in other languages, but it's usually done with a bunch of language constructs that essentially take the powerful generality of macros and break them up into separate features, bastardize them, and sprinkle them around the language, and such domain specific languages are usually quite weird, brittle, and composed of lots of little tricks to get them working. Ruby is not an acceptable Lisp just because it has some nice syntax for things.
    Runtime Dynamicism
    the ability to redefine a function, method, or data type, or evaluate an expression, within the context of a running process, and have the whole thing change immediately, without built system hot reloading tricks or incremental compilation or anything of the sort is crucial for something like Emacs.
    Regular Syntax
    Emacs has structural editing commands built into its core. The syntax that gives those structural editing commands the most powerful functionality with the least parser complexity is Lisp, or something similar, hands down.
    Homoiconicity
    The ability to represent actual, executable code, in a format that can be meaningfully structurally manipulated, walked, and modified – that is, linked lists of symbols – as well as just evaluated, as opposed to just as raw strings you have to munge into the right shape and then pray work when you evaluate them, is crucial to functionality like the unity of user interface and programming paradigm. For instance, the ability to capture the actual resultant code the user executes (impossible if you have no representation of code as data) and use it in a data structure that the user can easily view and edit, and then ask Emacs to programmatically manipulate to produce a reusable command that's just like every other function or command reliably, is key to having keyboard macros as powerful as Emacs does. Without such homoiconicity, user actions would have to be represented as new, custom data structures that are then interpreted to be run later by code written in the language the system is actually written in: a mini sub-language that creates distance between the user and the internals.
  • Things that it has uniquely:

    Advice
    Having the ability to advise the behavior of any function in the language at a native level, instead of having to declare advisable functions in a special way or something, as you'd have to do in any language where this was not built into the ground level, is extremely important. See the section on composable modifications.
    Buffers
    Having the concept of buffers built into the language virtual machine, instead of implemented as a library, ensures that buffer manipulation is literally as fast as it can get; moreover it makes it easier for buffer manipulation to form the core language primitive for text manipulation, which benefits user-system homoiconicity and also performance at text manipulation, since buffers are backed by a special data structure designed for fast text manipulation that's much faster than the regular strings other languages use (in this case, gap buffers, but as far as Emacs Lisp is concerned, that's just an implementation detail).
    Buffer-Local Variables
    The ability for some global state variables in Emacs Lisp to be transparently buffer-local (as in, can map to different values depending on which buffer the current code is executing in) is crucial for encapsulation, composability, and generality of Emacs modes and functionality, and it would be almost impossible to achieve ergonomically without that concept being built into the language – which in turn requires buffers themselves to be built into the language.
    Propertized Strings
    The fact that the native, first-class, default string data type in Emacs Lisp is propertized text is deeply important to it being able to ergonomically integrate with Emacs's fundamental paradigm. No other language has this.
    Dynamic Scope
    being able to temporarily redefine a variable or function just for the tree of function calls below a scope is incredibly useful for flexibility, customization, and malleability. Pure lexical scope is simply too brittle by itself!
    Simplicity
    Despite being quite powerful – honestly a good step above most modern scripting languages in power, despite being "merely a domain-specific language", a fact which is what enables such incredible applications to be regularly written in the Emacs computing environment – Emacs Lisp is at its core a very simple and consistent language. I think this is important for learnability.

Now, is this to say that Emacs Lisp can't be improved, or that it's the best language ever? Dear god no, far from it!<<Emacs Lisp Improvements>>

  1. Its standard library, while incredibly complete and featureful for the purpose, is full of the same inconsistent naming and argument ordering as Common Lisp's. I think a major naming and argument ordering overhaul could be very helpful to avoid common mistakes (although I don't see how this could happen).
  2. Without cl-lib and eieio, Emacs Lisp's standard library, while rich in other ways, lacks crucial functionality for things such as list manipulation, loop comprehension, conditionals, an object system, and generic multimethods; thus the attempt to discourage the use of those already built in and quite performant libraries out of a dislike for Common Lisp idioms (and concerns over "bloat" which somehow didn't apply to the creation of a second duplicate standard library with seq.el) has led to a proliferation of alternative standard libraries, or at least components of such, such as dash.el and seq.el, which are:

    1. in the former's case, not built in, which leads to larger startup times and slower execution due to dependency bloat all for functions which duplicate functionality already available within Emacs,
    2. in both cases less in line with the Emacs Lisp programming style historically, based on what's been around longer, what has been used the most, and Emacs Lisp's shared heritage with Common Lisp, and
    3. at least in the latter's case, much slower1.

    I think switching to encouraging a more Common Lisp style of programming, including making copious use of cl-lib would solve this problem (the opposite of Stallman's suggestions, ironically, because I don't think the "simpler" and "more elegant" libraries like seq.el provide complete enough functionality in the way the Common Lisp extensions do, so it'd just lead to a further proliferation of libraries).

  3. And of course there's the lamentable fact that Emacs Lisp is a Lisp-2 – I think the weight of the technical arguments falls strongly in favor of Lisp-1s; however, it's far too late to change this aspect of Emacs Lisp, and it's also far from the disaster many think it is.
  4. It's also pretty slow, even with native compilation (although not slower than Python!), which is obviously a problem when you're writing tens of thousands, hundreds of thousands or even millions of lines of complex code in it.

In all, however, I think these issues must be fixed without replacing, removing, superseding, significantly changing, or deprecating Emacs Lisp. Both because of the unique features of Emacs Lisp listed above, and because one of the biggest strengths of Emacs is the 40 years of package development that has occurred for it, all of which was done in Emacs Lisp and depends on that language intimately. Losing incredible, killer packages like paredit, smartparens, org-mode, org-roam, magit, elfeed, SLIME, CIDER, web-mode, AuCTeX, Emacs Speaks Statistics, emacspeak, mu4e, Proof General, citar, and the many other killer applications that have been created for Emacs that regularly convert users of other editors and attract laypeople, would be a complete and utter disaster. The existence of these packages is part of the core appeal of GNU Emacs, what makes it worth engaging with in the first place!

I also think that trying to introduce other languages – even equally domain-specific ones – alongside Emacs Lisp would be a mistake, since it would ruin many of the important properties I've been talking about above, such as the total homogeneity of the Emacs computing environment.

What I want is not the replacement or elimination of Emacs Lisp, nor a rewrite of the Emacs core into Rust, Common Lisp, or RnRS Scheme, then – what I want is the evolution of Emacs Lisp towards a sort of Common Lisp sibling language that is slightly simpler and has Emacs extensions, and then the gradual assimilation of the core into Emacs Lisp as it grows faster. Emacs should be Emacs Lisp. What I want least of all is for Emacs to become a polyglot environment, where impedance mismatches make inspecting, debugging, integrating, interoperating, and dynamically modifying various packages difficult, and where I have to learn a new language for every package I might want to contribute to.

In light of all this, I think the new Guile Emacs has the right idea in this regard: replace the engine under Emacs Lisp with something better – allowing better performance and more expressiveness, and homogeneous interface with similar Lisp languages – while keeping Emacs Lisp the same, but opening the door toward evolution down the road. I also think that the Emacs maintainers have been making solid strides toward this already with the introduction of lexical scoping by default, cl-lib itself, native compilation, and probably in Emacs 31, MPS!

Also, on a personal note, I not only think the "nicer language du jour" – Python, JavaScript, Scheme, or Lua – can't do the job as well as Emacs Lisp does, I also just like them a lot less than basically any Lisp. And I just find Emacs Lisp a complete and utter joy to use, despite its warts, in a way I don't enjoy those languages.