Hacker News new | ask | show | jobs
by rafram 306 days ago
The flaw in that metaphor is that elisp is a pretty suboptimal programming language for general-purpose programming. The standard library (in my limited experience) seems to use buffers as the base primitive and doesn’t help you very much if you want to do anything complicated without touching the current buffer.
5 comments

This is a common misunderstanding about buffers. Buffers are the base primitive for text manipulation for a good reason; it’s a deliberate design decision not a handicap or a straitjacket. Think of a buffer as a string with a cursor in it, where it is always easy to manipulate the text at the cursor. (It’s implemented using a gap buffer <https://en.wikipedia.org/wiki/Gap_buffer>, but you don’t need to know that.)

The operations you can do on a buffer include not just the usual string primitives like insertion and deletion of text, but _any_ editing command that you could use as a user as well. For example, if you want to move the cursor to the beginning of the line, call `beginning-of-line`. This is exactly the same function that is bound to `C-a`. Want to move to the next line? That’s `next-line`, which is bound to the `down` key by default. Want to drop the mark at the cursor position? Call `set-mark`. Want to search for something? `re-search-forward`. Now that you’ve found what you were looking for, you want to remove it from the buffer? Call `kill-region`, same as if you had typed `C-w`.

And commands build on other, more primitive, commands to give you complex behaviors. There are commands for creating, parsing, and sending emails, making HTTP requests and parsing HTTP responses, creating and parsing JSON, XML or HTML, etc, etc. You can create some JSON in the current buffer, send it off to the server via HTTP, and when the response comes back it swaps you over to a new buffer with the response. Or you can ask it to parse the response and just give you the body, etc. Basically any command you could interactively use as an Emacs user will work as part of a new Elisp program simply by virtue of the fact that it works on the current buffer. And the new Elisp programs you write can be used as commands by the user, and as parts of the user’s next Elisp program.

And switching buffers it not slow; all the buffers are in memory at the same time and the “current” buffer is just a pointer. The only quirk is that the pointer is stored in a variable instead of being passed as an argument to every single command you run. And that’s a good thing, because you would soon get tired of passing the current buffer to every single function you call!

I hadn't even realized how much I'd gotten used to emacs' way of thinking about buffers until I tried automating Google Docs.

... What a nightmare. Everything is an object. A header is an object. A paragraph is an object. There's a three-deep hierarchy to start talking about the current document. Yes, it's nice and namespaced and in that sense is well-defined, but the "hello world" of inserting text at the current cursor position is an absolute nightmare. And if you want to insert something more complex, like a couple headers and a paragraph and maybe a table? Get ready to build a factory function that creates a bunch of stuff and glues it together like you're writing a UI in Java AWT. And then it's not even in the doc yet, you have to go find your cursor, find the doc element the cursor is in, and only then can you insert content via a reparenting operation.

In emacs, you could do all of that with repeated "insert" calls, nearly literally touching the same keys you'd use to do it in the editor. It's an almost 1-to-1 metaphor mapping. If you know how to write text in emacs you know half of what you need to script emacs.

Lego is a "programming environment," so avoid the obvious contrarian nit of "must be more general," and accept there's no better language-environment combo for constructing workflows than elisp and emacs -- yes, it's leaps better than bash and term.
There is: my OS plus Python, Ruby, even JavaScript, or any other modern scripting language. macOS even supports Emacs keys globally!
You're confused on both points:

First of all, macOS doesn't "support Emacs keys" - those are readline/bash keybindings (C-a, C-e, C-k, etc.), not even partial Emacs navigation. The distinction matters. Emacs is inherently a modal editor, and no OS provides that aspect out of the box.

Secondly, although it sounds reasonable in theory, in practice it just doesn't manifest to the same level - because of the enormous context switching cost. While Python/Ruby/JS are excellent languages, using them for workflow automation typically means:

- Fragmented toolchain: Editor → terminal → file manager → browser → back to editor

- State loss: Each tool maintains separate state, history, and context

- Interface friction: Different keybindings, different ways to access data

Emacs provides unified state and interface - your scripts can directly manipulate buffers, access file system, interact with running processes, and integrate with your editing session. The "programming environment" aspect means your automation tools live in the same space as your work.

The fallacy is treating this as purely a language comparison when it's really about environmental integration. A Python script that opens files is fundamentally different from an Emacs function that operates on buffers you're already working with.

For personal workflow automation where you're already living in Emacs, the integration advantage is real.

Here some practical examples that would be just outright painful to achieve with a general-purpose PLs.

- Grab url from browser, insert formatted markdown link at point

- Extract all TODO items from project files, create agenda in new buffer (I can probably do it in fewer than 15 lines of Elisp without ever having to save, lint, compile that code)

- You're editing a CSV, write a function that operates on the current line/region and transforms it in place

- Mid-email composition, grab a code snippet from the adjacent buffer, format it, and insert

- While reviewing a log file, extract error patterns and automatically open related source files

Your automation remembers what files you had open, your cursor positions, your window layout. Scripts can modify your current work context rather than creating separate outputs

With Python/shell scripts, you'd need complex IPC, temporary files, external tools to achieve similar seamless integration with your active work session. That's why majority of programmers just don't even bother to think to try. Experienced Emacs hackers wouldn't even blink - they'd start whipping up Elisp like a chef would crack eggs to make an omelette.

Don't really see how the string (and other usual container types) or filesystem APIs are lacking in any significant way compared to stdlibs of other scripting languages.

I also believe that buffer as an abstraction strictly makes many harder things easier, to the point I often wonder about creating a native library based on elisp buffer manipulation APIs alone that could be embedded in other runtimes instead. So the without touching buffer is a premise/constraint I don't quite understand to begin with.

That's actually the zen of programming emacs: don't avoid using buffers; create as many of them as you need.

"But that's like opening a document every time I want to glue two strings together!"

Not at all. A buffer is just a fancy blob of RAM. It's not file-backed unless you make it file-backed. They do take up RAM, but you're programming on a modern computer, not a PDP-11; if you're comfortable with Python using a whole in-memory object to represent an integer, you're comfortable with buffers.

"But it's messy to leave them lying around."

It's a feature. Yes, buffers aren't well-encapsulated and if your program crashes mid-run they get left open. That's by design. You don't need encapsulation because you're not doing multithreading here (and if you are, there are primitives for that and they take a bit more work to use); emacs is for editing and there's only one you, so if the current program is creating buffers and has no way to run two copies of itself at once, who cares. And your program crashing leaving buffers around is a feature; you can inspect the buffer and see what it looked like at crash-time, or set up the buffers the way you want them before firing off the program to get the desired effect (try those tricks with most languages without slapping on a debugger). And there are scripting blocks to create temp buffers and clean up your buffers for you anyway.

"But it's weird to have two ways to talk about strings in the language!"

That's true; it's a bit weird to have the string primitives and also buffers. But that's a pretty common flavor of weird; Java has strings and also has StringBuilder. My rule of thumb is "any time I'd reach for StringBuilder in Java, I should probably consider using a buffer in elisp."

I agree with you, therefore I am pretty sure you meant to reply to the parent I was also replying to.
Confirmed. I wasn't disagreeing with you; it was a "yes-and" to what you were saying.
Elisp is pretty good at what it allows for: quickly hacking code.

Buffers are quite good as a primitive: almost every kind of information snapshot can fit in a buffer. It’s kind of a blackboard in a math room.

> elisp is a pretty suboptimal programming language for general-purpose programming.

Well, because it ain't. it isn't a general-purpose language. It serves a single, specific purpose and excels at that function remarkably well.

> if you want to do anything complicated

Then you'd delegate the task to more specialized tools, possibly written in general-purpose PLs. Which Elisp is not.

I was responding to the argument that Emacs is "a great operating system... if only it had a decent text editor." Not such a great operating system if its native language only works well for manipulating its could-be-better text editor.
The first part of that joke is an exaggeration. The second part of "not-so-great editor" is simply incorrect.

Emacs does provide some OS-like features - process management, file system interface, window management, runtime environment, but it lacks tons of other things to be even close to an OS - there's no direct hardware control, no memory protection between different parts, no multi-user support with security isolation, no device drivers, no network stack management, etc.

The "lacks a decent text editor" part is more unfair than the OS comparison. Emacs objectively has a very capable editor - stating otherwise is a sign of unfamiliarity and ignorance about it.

The joke is a relic from the 1990s and it's aimed to capture how Emacs (used to) prioritize power and extensibility over immediate usability - making it simultaneously impressive and frustrating - back in the day.

The joke is hilariously outdated along with another one - "8MB and constantly swapping" - Emacs went from resource hog to surprisingly lightweight without changing much - the world just got more bloated around it.

I'm just baffled why programmers are so notorious for holding onto outdated conventional beliefs that no longer hold any truth about them. Especially for cases like this, when it was clearly a joke. A joke that has not aged well at all.

Absolutely, I've been a die-hard Emacs user since about 2007 or so. I was a vim user for about ten years or so before that. It's absolutely an out-dated joke. Don't take it seriously.

It's not an OS. The text editor is great.

I was just using it to illustrate a point.