Clojure-like lambda expressions in Emacs Lisp ¶
If it's this easy, why isn't it implemented?
Anonymous function syntax
In Clojure[1] there are two syntaxes for anonymous functions. The first one is
equivalent to lambda
expressions in Emacs Lisp; they look like this:
The other syntax, which is shorter and built into the reader, has no equivalent in Emacs Lisp. It looks like this:
This is equivalent to the following lambda expression:
The shorter syntax is convenient and works really well with map
/mapcar
and other higher-order functions. It is, however, absent in Emacs Lisp.
Some time ago I found an implementation of short lambda syntax for Emacs Lisp. It's a simple macro which expands to lambda expression. You can test it like this:
The implementation, however, is incomplete. In the words of the author:
This assumes that there is a reader macro in the Emacs C code that
translates #(STRUCTURE) to (short-lambda STRUCTURE), in the same
way as for the backquote
macro.
Indeed, as it is, it's even longer than normal lambda
- clearly there's
something missing. Namely: a support for the short lambda in the reader,
which is implemented in C.
Implementing the missing part
How hard would it be to add the missing support to the reader? When I was thinking about this, I noticed that there is already something similar in Elisp, namely - hash-table syntax. It looks like this:
The #s(...)
syntax is supported by the reader, ie. hash tables can be read
with the same syntax they are printed in.
Wouldn't it be easy to create a short lambda syntax by changing #s
to
#f
? Turns out - yes, it is easy. It took me an afternoon or two to figure
it out, with no prior experience with Emacs C codebase. The whole
implementation is less than 5 lines of code.
Changing the reader
First, I located the hash-table reader code in lread.c
, which is where the
reader lives. There's a function called read1
, where the hash-table
support is implemented. It looks like this (reformatted for brevity):
The only thing needed to add support for short lambda is to change the line 19 to this:
The list2
function creates a list of two elements, where the first one is
a symbol, defined (that's the second, and last, change to C code) like this at
the end of lread.c
(fn
is and alias for short-lambda
):
The second element of the list is whatever can be read after the opening paren.
The whole diff looks like this:
When is it useful?
As mentioned, the shortened syntax works well with higher-order functions.
It's not essential, but it is convenient. Especially if you use libraries
like dash.el
, which give you a lot of such functions.
Just yesterday I was writing a bit of code to replace literal characters with HTML entities. I fetched the list of entities from the gist published by Christian Tietze (blog post, the gist), and started coding. I had to parse the list of entities and needed to break each line into components, entity, entity name, and description. The whole code looks like this:
There are 4 lambdas in the code - were it not for the short lambda, I would probably write this code differently. Using them, though, the code ended up being short and readable, without the need for any of the heavier abstractions.
That's it, so... why?
That's really everything you need to add short lambda support Emacs Lisp. I
have this implemented in my Emacs since a few years back and I use the
#f()
syntax regularly. It's convenient. It's easy to implement. I wonder
from time to time - why isn't it still implemented in Emacs?
Please let me know if you know the reason!
EDIT: so, um, yeah, one reason may be that nobody suggested
this as a feature yet. I'm stupid, it totally slipped my mind. I assumed it
was proposed already, for sure, given that the short-lambda.el
repo[2] is 6 years old at this point. But I didn't check. My bad!
UPDATE: rewritten, non-recursive reader in current master (2022-09-22)
When I pulled Emacs repo the other day and built it, I realized that my short-lambda stopped working. Apparently, the reader got rewritten to be nonrecursive back in May, and my patch obviously stopped being applicable.
The good news is that it wasn't too hard to patch the new reader - it's still just a few lines of code:
- https://www.clojure.org/guides/learn/functions#_anonymous_function_syntax ↵
- https://github.com/abo-abo/short-lambda/blob/master/short-lambda.el ↵