It’s remarkable just how many types of programming are possible with Emacs lisp. In fact, it’s hard to find a style of programming that isn’t possible. I’ve translated a few examples from other languages into elisp to showcase this diversity.
Iterating over a sequence
Suppose we want to double every item in a list. In Ruby, we might write it like this, using map:
In Elisp, we can use mapcar
:
In Python, we might use a list comprehension:
Elisp has the loop
macro (originally from Common Lisp):
In Scala, we can use special arguments to denote the function arguments:
Using anaphoric macros we can do this in elisp:
Searching a sequence
Suppose we want to find the first integer in a list that’s lower than some value. In Java, we’d probably use a for-each loop, terminating as soon as we find the value we’re looking for.
We can do this in elisp using dolist
and return
:
In Haskell, we’d write this in a functional style, composing functions:
We can do this in elisp too:
Function arguments
Suppose we want to find the mean of several numbers. In C, we might write:
No sweat in elisp:
However, C doesn’t support variadic functions without also passing in the number of arguments. In many other languages, we can write a function that takes any number of arguments. In JavaScript:
Elisp supports variadic functions out of the box. This gives us a straightforward translation:
In Python, we might use keyword arguments when calling functions. This can make function calls clearer (we can see which argument is which) and facilitates optional arguments:
We can use defun*
to do this in elisp:
Destructuring and Pattern Matching
In CoffeeScript, it’s possible to destructure an array like this:
Elisp has you covered:
In functional languages like Ocaml, we can use a more general technique of pattern matching:
Elisp can do pattern matching too, with pcase
:
Monads
Monads, as popularised by Haskell, don’t really make sense without type classes. However, the Maybe monad has a natural elisp equivalent. An inexperienced Haskell programmer might write:
There’s a lot of wrapping and unwrapping here, which a monad can do for us:
(An experienced Haskeller would just use liftM2 (+)
, but that’s not
relevant here.)
dash.el provides -when-let*
(equivalent to Scheme’s and-let
) which
allows us to mimic this behaviour:
Objects
A classic example of a class-based code structure might be a monster in a game:
Elisp has EIEIO (Enhanced Implementation of Emacs Interpreted Objects), which is an implementation of CLOS (the Common Lisp Object System). So, we can straightforwardly translate this:
No discussion of object-oriented code would be complete, of course, without an example of class-based inheritance:
EIEIO version:
Finally, EIEIO also has support for more exotic object-oriented features, such as mixins:
EIEIO:
Namespaces
Elisp has separate namespaces for functions and variables. So if we
store a function in a variable, we have to use funcall
to use
it. Scheme, however, is a lisp-1 with a single namespace. In Scheme we
can write:
With a short macro, we can actually execute this code unchanged:
In Clojure, we can use explicit namespaces to separate code. This prevents us having to worry about name clashes.
There’s a codex.el package that allows us to do this in elisp:
What elisp doesn’t have
Elisp can’t do everything. There are some languages features that simply can’t be implemented by the users. There are no reader macros, there’s no FFI, and there’s no multithreading (though threads are being worked on).
There are also powerful language features that could be implemented, but haven’t yet been implemented in elisp. For example, metaclasses (implemented in CLOS), hygienic macros (implemented in Common Lisp), logic programming (implemented in Clojure) or even a type system (implemented in Scheme).
Should I write code like this?
For the features I have demonstrated, some examples are carefully
chosen to showcase the capabilities of elisp. For example, the
define
macro still won’t allow you to write ((foo) bar)
, it’s a
syntax error. Other examples are impractical (codex.el currently makes
edebug unusable), whilst still others are so rarely used that other
elisp developers will need time to understand the code.
Used in moderation however, these are all excellent tools which an elisp programmer can use. He or she can bend the language according to the problem at hand, rather than the other way round.
All that aside, elisp is an immensely flexible, deeply hackable language. Not only does it enable you to be productive extremely quickly (“learning any amount of elisp makes your life better immediately”), it also provides a whole zoo of language features, providing an elegant way of expressing virtually any program.