Compared to extending via workarounds, extending in “pure Lisp” can be both easier and harder, as we are still bounded by coding conventions and existing code, and one cannot possibly extend everything without breaking some of them.
Let’s start by overriding a single function. For example, when exporting Org-mode files to HTML, Org-mode defaults to generating random HTML ID anchors. To change that, you just override the org-export-get-reference function that generates the IDs, right?
(advice-add #'org-export-get-reference :around #'org-html-stable-ids--get-reference)
Oh no! It turns out that, sometimes Org-mode directly calls org-html--reference, bypassing our override. That means we also need to redirect org-html--reference:
(advice-add #'org-html--reference :override #'org-html-stable-ids--reference)
Problem solved? No. Conventionally, Emacs Lisp code uses double dashes to tell the users “this function is internal”, as is in the org-html--reference name. Yes, by being free to extend any part of the editor, you are free to modify any internal functions or states, in a way that may or may not be problematic under specific circumstances, with code that can be broken in any future updates.
And it’s not the end of it. The el-patch package allows you to apply “patches” on most any Lisp code to modify behaviours nested deep inside a function:
;; Original function
(defun company-statistics--load ()
"Restore statistics."
(load company-statistics-file 'noerror nil 'nosuffix))
;; Patching
(el-patch-feature company-statistics)
(with-eval-after-load 'company-statistics
(el-patch-defun company-statistics--load ()
"Restore statistics."
(load company-statistics-file 'noerror
;; The patch
(el-patch-swap nil 'nomessage)
'nosuffix)))
;; Patched version
(defun company-statistics--load ()
"Restore statistics."
(load company-statistics-file 'noerror 'nomessage 'nosuffix))
Luckily, el-patch provides el-patch-validate so that you can worry less about your patches going ineffective or unexpectedly destructive. But you still need to maintain all your patches if anything goes wrong.
Any extensible system is not void of these problems. If you impose strong enough encapsulation, then eventually something can’t get customized; if you expose everything, well, good luck keeping backward compatibility (as the system maintainer) or forward compatibility (as the user doing your modifications). By making it possible to “extend any part of the editor,” you are literally making any part of your code unextensible, and now “every change breaks someone’s workflow.”
Emacs’s cross-language isolation/API might not be perfect, but I’m very grateful for it. If Emacs were written in pure Lisp code and anything is extensible, my work-in-progress Emacs clone couldn’t be remotely possible (because we do want to rid some of the spacebar heating problems while keeping some compatibility).