Agree. Emacs lisp is crappy for silly reasons—archaic names and lack of complete or consistent standard library, but I think the language fundamentally gets a lot of things right, especially for the domain of a user-customisable text editor.
Quick comparison to some other languages:
- vimscript: lol
- JavaScript: the lack of dynamic scoping may be tricky for extensibility, and not having macros is a shame (eg save-excursion and friends). But the language is mostly reasonable. One interesting aspect of elisp is that functions are typically just the symbols of their names, so it is easy to inspect and modify lists of hooks. I also worry about the pervasive use of modularity constructs like IIAFs—a great feature of elisp is the ability to poke and advise other code
- C or Rust: I think it is too hard to do something small or hacky in these languages and the constraints of type systems and manual memory management make ad-hoc extensibility hard.
I’ve also seen emacs (module) extensions written in a strongly typed functional language and they sort of work but can be fragile (eg advising a function but making incorrect assumptions about their types), or needing to use functions and closures for common operations like switching buffer or saving and restoring state. (It also amazes me that more languages don’t have unwind-protect and predictable evaluation order as standard, but that’s another topic)
Something similar is sometimes discussed under the term "aspect oriented programming". That might help with your Google adventures.
(I know there was one black magic implementation of it for C# on Windows at some point and also similar black magic for Java, but that's as far as I've ever explored it other than in Emacs Lisp. (Black magic in this case refers to splicing in the advice in the compiled bytecode of the program.))
Edit: apparently there's some basic implementation for Perl, too, and it's documentation looks like a neat intro to the idea: https://metacpan.org/pod/Aspect
> I have to put the advice at the spot of what I'm advising there.
I'm not sure what you mean by this. The idea with aspect-oriented programming is that you can create a bundle of functionality that extends existing functionality but exists as its own separate bundle.
In other words, you specifically do not have to put the advice at the same place as the adviced. That would just be regular procedural/functional/objevt-oriented programming, wouldn't it?
No. The advice in AspectJ is fully self-contained. You specify -- completely separately in its own place -- what the advice is, and what it is advising. You can advice things that are out of your control.
This is indeed the idea of aspect-oriented programming. I'm not sure where you got a different idea. Could you point me that way?
In the use of aspectj that I have ever seen, it was always an annotation based thing. I could annotate a method as getting logged to get a trace level log. Yes, the logic of doing the trace was somewhere else, but I couldn't place a trace on any invocation of a named method. Say I wanted to log all calls to (make-hash-table), in elisp, that is an easy advice call. In java, I didn't think that was really doable.
That said, thinking on some of the tricks I'm ok with in tests, I guess it has to be possible. Not sure what the code looks like.
It is amusing, as even the annotation based stuff I've seen is near universally disliked by engineers with experience from it. Seems a very common foot gun, in practice.
(I think you are implying that functions would care about properties of this and that one would achieve “dynamic scoping” by modifying what this is.)
The first issue is that this doesn’t go deep in the stack. E.g. if you want to affect the option used by the foo function on a whole list of objects, it is hard. If you want to affect a call deeper in the stack that can be very hard (elisp example: some extension might want to switch to a buffer but you might want it to pop up the buffer or make some other windowing choice. The solution is that by dynamically binding certain variables around where this extension/function is called, you can make switch-to-buffer call display-buffer and you can override the way display-buffer behaves)
The second issue is that it is ugly.
Compare the elisp example of:
(let ((option t))
(bar-foo bar))
bar-foo can call baz which can call whizz which could then depend on the value of option.
Quick comparison to some other languages:
- vimscript: lol
- JavaScript: the lack of dynamic scoping may be tricky for extensibility, and not having macros is a shame (eg save-excursion and friends). But the language is mostly reasonable. One interesting aspect of elisp is that functions are typically just the symbols of their names, so it is easy to inspect and modify lists of hooks. I also worry about the pervasive use of modularity constructs like IIAFs—a great feature of elisp is the ability to poke and advise other code
- C or Rust: I think it is too hard to do something small or hacky in these languages and the constraints of type systems and manual memory management make ad-hoc extensibility hard.
I’ve also seen emacs (module) extensions written in a strongly typed functional language and they sort of work but can be fragile (eg advising a function but making incorrect assumptions about their types), or needing to use functions and closures for common operations like switching buffer or saving and restoring state. (It also amazes me that more languages don’t have unwind-protect and predictable evaluation order as standard, but that’s another topic)