Back to the talks Previous by track: Sunday opening remarks Next by track: An introduction to the Emacs Reader Track: General

Some problems of modernizing Emacs

Eduardo Ochs (he/him) - Pronunciation: Oks, IRC: edrx, http://anggtwu.net/, more info at http://anggtwu.net/contact.html., eduardoochs@gmail.com

Format: 26-min talk ; Q&A: IRC Etherpad: https://pad.emacsconf.org/2025-modern
Etherpad: https://pad.emacsconf.org/2025-modern
Status: Q&A to be extracted from the room recordings

00:00.000 Introduction 01:05.000 The main themes of this video 02:18.000 Inner views of Emacs objects 02:54.000 Older Emacses 03:35.000 Newer Emacses 05:30.000 Help buttons 06:41.000 Anyone can learn Lisp in one day 09:34.000 Lambdas for beginners broken 12:59.302 Demo 17:29.120 Printing something in different ways for lambdas 21:52.840 Exploring buttons 22:42.160 Some design decisions behind eev 24:05.800 Tests

Duration: 25:22 minutes

Description

https://anggtwu.net/emacsconf2025.html

This talk is going to be a reworked version of the incomplete video in http://anggtwu.net/2025-modern.html. I will start by presenting several notions of "simplicity" and "elegance", and show that when I started learning Elisp it was "simple" and "elegant" in a way that it no longer is; then I will show how to fix some tiny parts of the problem by 1) using functions based on `cl-prin1', 2) redefining some printing methods with "(cl-defmethod cl-print-object …)", and 3) using Common Lisp to understand some recent parts of Elisp that are not well-documented.

About the speaker:

Eduardo is the author of an Emacs package called eev that makes total sense to a handful of people and no sense at all to practically everyone else - except for one part of eev, called "eepitch". He intends to explain the reasons for that in his talk.

Discussion / notes

  • Q:  [from IRC] why the lambda representation has recently changed to vector-like?
    • A: It is easier to debug Emacs if different kinds of objects have different types... keeping everything as lists is better for quick-and-dirty code, but the developers wanted something that felt less "dirty"...
  • Q: Do you have hints for integrating eev into an already existing workflow?
    • A: I need to understand the other workflows to see what I can recommend... can we discuss that on chat? If your workflow includes Org code blocks then I will learn a lot from your workflow too...
    • I was hoping for some document with some general notes or ideas. Right now eev feels very 'dominant'. While it works great I find it difficult to mix eev and other Emacs tools
    • Which other tools? Can we discuss examples? I was able to integrate eev with practically all the other packages the I know...
  • Q: [from IRC] What do you think of org-mode and its executable src blocks?  That seems to overlap with what eev seemed to provide.
    • A: I need to learn more about Org and source blocks!!! The problem is that every time that I tried to learn source blocks I started to ask many technical questions about the details and got lost in a big maze of rabbit holes... see: <https://anggtwu.net/2021-org-for-non-users.html> - I even tried to write functions that would display the data structures behind source blocks, but at some point I gave up and decided that I would only come back when I had people who could help me... =(
    • A: in short: when I tried to use Org mode I was always asking the wrong questions and trying to understand internal details that the developers treated as very advanced
  • Q: I just tested on SBCL, Guile and Chez Scheme, and all of them use opaque objects for functions (Chez: #<procedure square>, SBCL: #<FUNCTION F>). Why is Emacs Lisp changing to using semi-opaque vectors considered so bad? (Or is just making them fully opaque better for beginners?)
    • Note: on SBCL you can inspect that function object and see both the lisp code and the machine code it compiles to
    • A: It is not bad at all!
  • Q: What defines `find-classtree'?

  • I must confess I am still confused about eev. same with hyperbole.

    • A: eev doesn't make any sense from the description and the sales blurbs, all the few people who learned it learned it from the tutorials. but that video has a big example that is very easy to run it the person has eev installed - it starts a bit after 13:00.
  • I often find myself wishing other systems had a help system like Emacs.
  • It took me about 5 years to move from Space & Doom to vanilla Emacs 🤣
    • Now that I think about it, it was about the same for me.
    • it was less about learning elisp and more about finding the motivation & internal justification to start from scratch. David Wilson's tutorials helped propel me as well
    • did you switch from vim/evil bindings to "Emacs Standard" bindings?
    • The Doom & Spacemacs Frameworks obfuscate how vanilla Emacs actually works... and so after a while it became a barrier
    • not really, I use evil when writing code but tend to stick to vanilla bindings for most other buffers
    • My own motivation was that one day Spacemacs broke an important org-mode keybinding (M-RET) in a way that I couldn't work around. I had managed to learn a decent amount of Elisp by then, so I decided to go try my first vanilla config.
    • I think it took me a few months
    • me similar: I use evil mode for all prose and programming buffers but Emacs modes for everything else.
    • I had installed too many packages that I didn't understand at all
    • I was really unhappy with performance as I started to rely more on LSP modes (this was before the bytecode implementation). Switching to vanilla & moving to Eglot really helped with that, then of course the bytecode changes make it a non-issue now
    • thanks! just started using emacs about 4-5 months ago via Doom. But I can't help think I've skipped some important learnings.
    • I was quite happy when I got my first taste of native compilation. It improved Elisp performance tremendously.
    • I really recommend giving vanilla a try! You can use this trick to setup a parallel config so you can try it out before scrapping your current setup: https://stackoverflow.com/a/58039656/315827
      • That SO strikes me as slightly more complicated that necessary.. I would usually do it inline, like: emacs -Q -eval '(setq user-emacs-directory "~/emacs.1.d")' [filename, maybe]
      • There is also the --init-dir flag.
    • Fair comment, with your version it could be nice to build a shell function or alias so you can have multiple configs at one time
      • shell function or alias is a great idea! taking a note to set that up for myself!
  • Q: following along a little using IELM... which version of Emacs was used in the recording? My (symbol-function 'foo) is giving #f(lambda (a b) [t] (+ a b))
    • A: 31
    • I get #[(a b) ((+ a b)) (t)] (not nil for the last vector element as shown in the presentation)
    • Emacs 31 would be a development build (vs from the release branch) so it's possible two different builds of 31 could have subtle differences depending on which commit was the last included in the given builds.
  • A: the demo is here
    • https://anggtwu.net/2025-modern/00-try-this.html
    • https://anggtwu.net/2025-modern/00-try-this
    • I think that people can run the demo with just this (find-wget "https://anggtwu.net/2025-modern/00-try-this") except for one line that with give an error because it depends on a function that is not in ELPA - actually not even on the github version yet...
    • if anyone has eev installed and wants to try it please tell me... I think that one line of the demo calls a function that is not in the version of eev that is ELPA yet
  • Q: What do the leading red * in the left window mean?
    • A: https://anggtwu.net/eepitch.html#test-blocks
  • I was using eev for a month or so then my guix package broke and I am too lazy to fix it
  • Very informative talk.
  • Thank you edrx! eev has been on my list of things to check for some time!
  • Super cool seeing pieces of the workflow with eev, gives me some ideas!

Transcript (unedited)

[00:00:00.000] Introduction
Hi, my name is Eduardo Ochs, and this is my video for the EmacsConf 2025. Its title is Some Problems of Modernizing Emacs, and that is the same title as a video that I tried to record in March. But my video from March had a good "beginning" and a bad "rest", and I thought, I can't release that because the rest is very bad. I need to replace... to re-record the last part of the video. But I never did that. So this video is going to be the first 12 minutes of that other video, with another ending. And in this other ending, I'm going to show some things that are very easy to test. And, if people are interested, then the rest of the old video will make more sense. Anyway, let me start. Hi! My name is Eduardo Ochs. I'm the author of an Emacs package called eev and the title of this video is "Some problems of modernizing Emacs".
[00:01:05.000] The main themes of this video
Here is a summary of the main themes of this video. I'm going to talk mainly about these four things here. The first one is that Emacs has changed a lot in its recent versions, and now it has lots of types... so if we want to look under the hood and to understand what Emacs really does we are going to stumble on lots of types... and the current tree of classes and types looks like this... that is, is quite big. The second theme is that people used to say things like "Anyone can learn Lisp in one day"... I'm going to explain this quote, and I'm also going to show that now this is gone... anyway. This is a very short summary... details soon. I will also show how to display better "inner views" of Emacs objects... I'm going to define what is an inner view, of course.
[00:02:18.000] Inner views of Emacs objects
The main trick is that we are going to use one of the ways of displaying internal objects, that is the cl-print family of functions, for example, cl-prin1-to-string, and here are some examples of the kind of output that we are going to see... for example, if we run these two lines here the first line defines a function foo and the second line sets o to the internal view of the definition of foo.
[00:02:54.000] Older Emacses
In older Emacses o would be just a list that looks... that would look very similar to this line here... but in newer Emacses the result of this - I mean, the the contents of o is this thing here, that looks quite different from this definition. So, in older Emacses the contents of the function cell of o... sorry, of the function cell of foo, would be an "old-style lambda", that would be just a list like this...
[00:03:35.000] Newer Emacses
and in newer Emacses uh the contents of O would be a "vector-like lambda"... look for the square brackets here - this is a vector, but it is preceded by a hash sign. So this is what we call a "vector-like lambda", and vector-like lambas do not have a canonical printed representation - they have at least two semicanonical printed representations... The first semicanonical printed representation is this one, that is generated by a family of functions with names like prin1... and the second semicanonical printed representation is like this - it looks like a list... it looks somewhat like this definition of foo here, but it has this :dynbind symbol here... and it turns out that when we use the cl-print family of functions we can reconfigure how things are printed... and I'm going to show several interesting ways of reconfiguring how lambdas are printed, and one of the ways is going to be like this. We can also use the cl-print functions with my indentation tricks to to display how types, or classes, are viewed internally by Emacs, and this is a big example... This is what Emacs considers as being the definition of the type cl-structure-class, class and it is this big thing here. I edited it very lightly... I just deleted some line breaks here.
[00:05:30.000] Help buttons
And another thing that I want to explain is that Emacs has some help functions that I have never liked... for most people they are good enough, but for me they aren't... they... uh, well - I'm going to say more about this later... and, for example, if we want a description of what is this type here, that we just saw in its internal view here... we can run either describe-type or my variant of describe-type, and we get a help buffer that looks like this, in which these blue things that are underlined are "buttons", in the classical sense... you can click on these buttons, or type RET on these buttons, and you will be taken to another help page, that is generated dynamically... and you can navigate back and forth... and well, whatever... and I'm going to explain my problems with these kinds of help buffers and what I'm trying to do to overcome these problems...
[00:06:41.000] Anyone can learn Lisp in one day
One of my slogans in this video is going to be this one: "Anyone can learn Lisp in one day". this is a part of a bigger quote that I took from a keynote presentation by Abelson and Sussman, who are two dinosaurs of Computer Science... Here is the full quote: "Anyone can learn Lisp in one day - except that if they already know Fortran then it would take three days." This is a frame of the video... By the way I am going to to add this... "and if the person is starting with Doom Emacs then it would take 5 years." why? I'm going to explain why. This is how Emacs used to be. If we execute these two expressions here the first one... sorry, each symbol can have two "values", one is its "value as a variable" and another one is its "value as a function"... and if we run this we store 42 in the "value cell" of the symbol foo, and if we run this defun here it stores a certain anonymous function in the "function cell" of the symbol foo... and in Emacs, until some time ago if we did that and and if we ran this expression here the result would be 42, because of this line here, and if we ran this line here the result would be the anonymous function corresponding to this defun here... but now this has changed... the result of this thing here is this vector-like lambda here - but that doesn't matter much now... So, until some time ago if we did that and if we ran this expression here, (foo foo)... Emacs would do this: it would replace the first foo by this anonymous function here, it would replace the second foo by the value of foo as a variable, that is 42, and it would evaluate this, and the result would be 420. So, again, we used to have this slogan here, "anyone can learn Lisp in one day"... but now this is gone. Let me show... let me talk a bit more about why...
[00:09:34.000] Lambdas for beginners broken
the title of this slide is "Lambdas for beginners broken"... if we run this, as I've shown in the previous slide... in the old style, in old Emacses, the result of (symbol-function 'foo) would be this anonymous function here... and now we get this strange thing here. So, this is an "old-style lambda", this is a "vector-like lambda", and until the middle of 2024 beginners could learn a lot of Lisp by thinking only in terms of objects like these... this is a function and this is an anonymous function, and they would learn how to draw cons cell diagrams like this thing here and this thing here... they would think on lists as being these trees here, and they would be able to understand a lot of Lisp just by thinking in these terms... and then vector-like lambdas started to appear in many places... and if we use "vector-like lambdas" in a wide sense, to mean all the new objects, these new objects, that are difficult to visualize... they also started to appear in many places. This is a continuation of the previous slide - this part here is a copy of things that were in the previous slide... before 2024 beginners could open black boxes like this... they could try to see what was in the function cell of the symbol foo... and they would see something elegant and mind-blowing... and they would start to love Lisp immediately. Now what they get - what they see - is a tiny part of a very complex structure that is very powerful but that is very difficult to understand... and now our beginners are overwhelmed instead of mind-blown. Note that I said "black box" here. Let me explain the term. We can open what's inside of foo... we can open foo to see the contents of the symbol foo, and we can try to see what's in the function cell of the symbol foo... so we can open the box, but what we get is something very difficult to understand, and so I'm going to say that when this happens that box is black. It is not totally black - we can open open it - but we don't understand what is going on there, so we declare that that is black. And... when these things started to happen I was overwhelmed - and in this video I'm going to pretend that I was not the only person that was overwhelmed by these new structures that are not so elegant as the ones that we had before. Anyway... In the beginning of the video, I said that I was going to replace the second part of my video from March by something that was very easy to test. So this is a quick demo for the very impatient. I'm recording this in December for the EmacsConf 2025. This is the demo that I'm going to present. The idea is that people can run the demo a first time just to check that everything works and to have a notion of how things look like, and then, in a second moment, they can rerun the demo more slowly to understand what each step does. So... we are in a file called "00-try-this"... and the idea is that we can execute most of this file just by typing f8​s in the right places. Remember that when we type f8 on a line that starts with two red stars, eev treats that line as a comment. So I'm going to start here... Note that it says in the bottom of the screen that this is a comment. We are going to run this to download some files... Now the files are there... This find-2a here shows a certain file at the window at the right, but we don't need to pay attention to that. And this thing load​s that file. So when we load that file, it defines some functions here that are going to be used by the rest of the examples. Now we can run this thing here... Note that we just defined some functions and then we ran these functions here... find-eoutput-2a... and they show some things in the window at the right. These things are boring. When we run adt-insert with argument 42, it just shows a 42, in this way... The other ones show other numbers... and so on. And... what happens when we modify this function here, adt-2, by adding and removing advices to it? The idea is that people can run this thing here several times, watching the window at the right, because the results are going to be shown there. So, in the first moment, when we run... no, no, sorry, sorry, let me run it again. In the first moment when we run adt-2 it just shows a 2, and then we modify it in a certain way, and we run it again, and now before showing the 2 it shows a 1, mysteriously, and then we add something to be run after the 2, And we run it again, and now adt-2 shows these three things. And then we remove the advices, we remove these other things, and when we run adt-2 again, it shows only ;; --&gt; 2. It's impossible to understand that in the first time, so we can run that several times... to see how things work. And now we want to understand what changes in the function adt-2... how it is modified internally. I'm calling that the internal view of the function, and we are going to compare several internal views of the function adt-2. I'm going to reset the function adt-2 by removing the advices and placing the advices on it again... and if we just pretty-print this function here, the symbol... the value of this symbol here as a function, it is something very ugly. But if we print it in another way, with cl-prin1, then we get something that is much nicer... but that is not indented. And if we use this thing here, cl-prin2 instead of cl-prin1, it becomes indented. So let's try it again. Here is the current view of what is adt-2. So, the original adt-insert is here... and here are some modifications that were added by the advices. And we can run these things many times to understand what each step does. But my suggestion is: in the first time just run everything very quickly... and then you run it again, paying attention to the parts that look more interesting.
[00:17:29.120] Printing something in different ways for lambdas
Now, I'm going to... Remember that here I printed the contents of adt-2 in several different ways, and now I'm going to show how we can do the same idea of printing something in different ways for lambdas... that is something that I explained in the first part of the video. In this part of the demo we define a function foo... this setq here defines o as the contents of the function cell of foo... And now we are going to print that o in several different ways. The default way is this one, it's very ugly, but we can redefine how these things are printed by just running these lines... and if we pay attention at what's happening at the window at the right, we can see that we have several different printed representations for the same thing... and then at the last step, we reset the printer to the default representation. And the details are here. If we run these lines here, they show the definitions at the window at the right. And this... now comes the difficult part, in which we have to do something besides just running things with F8. We need a help buffer with buttons... buttons in the traditional sense, and we need to choose a certain button there, or any button there, and run M-x ee-set-button on that button. So, let me define a struct here, and this is some help on what is that structure. I'm going to choose this button here and I'm going to type M-x ee-set-button. The message is a bit obscure. And now we have something that displays a lot of information about that button... And we can also run that with just F8s. By the way, if we want to understand the code that's behind these things, we can run this sexp here. It is going to show the code here at the right. But anyway, these first lines here... they display the output in the echo area... Let's try... And each one of them extracts a different part of the information on that button. And these other lines here create a three-window setting in which the help buffer is shown here, and the result of some other thing is shown in the third window. Let's try... So now that we have... well... I said that we needed to choose a certain button and run M-x ee-set-button there. We have done that... so now this variable ee-button contains information about the button... And now we can run this part here as many times as we want to... try to understand what are the values of these things here... and how some things start with a value that is very complex and very difficult to understand, and then we extract the more interesting parts. And the details, as I said, are here. That was the end of the demo, and my question is, what was your reaction to that? If your reaction was more like "wow" than like "blergh" then you might like the last part of the video that I recorded in March, that was very technical... When I recorded it, I thought, oh my god, this video is very bad... only the hardcore eev users are going to like that, and there are less than five hardcore eev users in the world...
[00:21:52.840] Exploring buttons
But anyway, if you saw that introduction and you think that these things are interesting, you can execute any one of these S-expressions here and take a look at the final part of that video that describes how I wrote some functions for exploring buttons. If you execute this sexp here, it plays a part of the video starting from that position. If you execute this one, you go to the subtitles in HTML... and if you run this one, you go to the subtitles in another format. And... that's it! No, no, sorry, I said "that's it", but I forgot a very important part...
[00:22:42.160] Some design decisions behind eev
I had prepared this slide here to explain some design decisions behind eev and why there are so few users of eev... and let me do that. The thing is that my working memory is very small, and I need examples that are easy to visualize... ideally, examples that are easy to run and that I can compare the outputs of different ways of running them. For me, understanding specifications in an API is not enough... examples work much better for me. Also, I make lots of mistakes when I type... so I need ways to run my executable notes by just typing f8 and M-e... and... most people in the #emacs channel and in other places recommend executing sexps by typing M-:, like this... But I hate that... I think that M-: and IELM are for people who type well, so not me... And also, 99% of what I do is scratch code. Very few things that I do go into "production" - between quotes. So most of my tests are not automated tests. They are just things that I run with F8, and they don't include the expected result. Many people feel that this is very, very, very wrong. Tests MUST MEAN automated tests. Also, and this is very frustrating, eev has lots of "non-users", I mean, people who have tried to use it, but they said that they couldn't figure out how to use it. They couldn't understand what would be the workflows for eev. And that's the same that happens with me with code blocks in Org... I'm still at the point in which code blocks in Org just feel wrong. I still don't understand how to organize my workflows around code blocks. I said that eev has very few users, and they are all very weird and very busy, and I decided that all my blog-ish posts about eev would be things that they can run in a few minutes... just like the demo that I showed a few minutes ago. I said "like the examples in the rest of the video", but I forgot this slide, and I'm recording this after the demo. So... that's it. Thanks!

Questions or comments? Please e-mail eduardoochs@gmail.com

Back to the talks Previous by track: Sunday opening remarks Next by track: An introduction to the Emacs Reader Track: General