Feeling gratitude towards this community. Just wrote my first #emacslisp advice ever.

(advice-add #'package-function-has-no-hook :before #'my-little-hook-function)

A small thing, but it removes friction from my org-static-blog (recommended!) workflow.

These little steps forward are what make this hobby so much fun. And—you'll just have to believe me—the confidence to bork around like this comes from being part of this community.

[EDIT: broken link removed; see thread]

#emacs #mastodon #fediverse

@jameshowell FYI if you wrote "my-little-hook-function", you could also be interested in `define-advice', which combines a defun and a advice-add into a single form. Here are two example from my init.el:

(define-advice eval-last-sexp (:before (&rest _) eval-last-sexp-pulse)
(save-excursion
(forward-sexp -1)
(let ((start (point)))
(forward-sexp 1)
(pulse-momentary-highlight-region start (point)))))

@jameshowell Also, what function from package.el did you want to modify? Your sourcehut link points to a 404 page.

@pkal Sorry! Let's try it this way. NEW BLOG POST: "My first advice! (in Emacs Lisp)"

https://jamesendreshowell.com/2026-04-04-my-first-advice-in-emacs-lisp.html

#emacs #emacslisp

My first advice! (in Emacs Lisp)

@jameshowell You ask in the blog post why Emacs Lisp doesn't have a function to return the contents of a file as a string. It kind of makes sense to me, let's see if I can explain it.

Emacs has two data structures to store text in: strings and buffers. You may think of buffers as just for showing to the user in a window to edit, but buffers also have an extensive collection of functions to work on them programatically (obviously, since every thing the user can do to edit a buffer just calls some command). I feel like Emacs in general pushes you towards using strings only for small amounts of temporary text and buffers for longer text or longer-lived pieces of text. From that point of view it makes perfect sense to me that Emacs has a function to insert the content of a file into a buffer, but not one to return it as a string: a file is likely to long and to stick around a while for you to work on it, it belongs most likely in a buffer rather than a string. The existence of `insert-file-contents` and the non-existence of a corresponding string-returning function is meant to nudge towards buffers for file processing.

@pkal

@oantolin No, I get it, in fact slinging HTML lines around in buffers and concatenating them together in buffers makes more sense—in the Emacs context. But you can see how someone (like perhaps the author of org-static-blog, and certainly naive me) would cling to string-oriented habits.

Thank you for the thoughtful, patient, illuminating answer to my snarky rhetorical question!

@pkal

@jameshowell Well, having given that answer, I should say that lots of people have probably had to write that function to get the contents of a file as a string, it probably should be included with Emacs. 😅 @pkal

@oantolin The "s" package has a function to do that, and the consequence is that people keep on reading an entire function in as a string and then doing GC-heavy list processing on the function, where using a buffer would have been more idiomatic.

I am not sure if I submitted a patch for this once, but a compromise of having a `slurp` macro could be interesting, since it would read in the file once at macroexpansion time, without making it a general replacement for buffers.

@jameshowell

@pkal @oantolin @jameshowell I vote that Emacs gets a read-file-to-string function! This has been requested so often by many people and there are valid use cases, despite the small performance risk if used incorrectly. I find these concerns exaggerated. Think about your recent addition ensure-proper-list, which is so far not even used in the Emacs code, and which is O(n) in contrast to ensure-list - another risk for incorrect use. Even worse there is almost no need for this function.
@pkal @oantolin @jameshowell I argue that API design should better be guided by actual demand, and not only by theoretical concerns. Emacs is first and foremost a practical tool. One could take a look at functions which are often used from the s.el or f.el packages and then add them to Emacs. Now we have Compat to port the functions back. This is a missed opportunity and my hope for Compat was specifically to close this gap.
@pkal @oantolin @jameshowell Another indication for demand is to just look at the code of Emacs and some packages and check if we could replace common patterns. It does not look like this in the case of ensure-proper-list, but I think I have seen places where a file to string function would have come handy, for instance if the file is small or the content of the file is used unchanged, or if the performance overhead is acceptable despite string manipulation. Buffers are not without cost either.
@pkal @oantolin @jameshowell So to conclude I think API design in Emacs could be informed better, by taking into account all the information that we have, theoretical considerations, inspiration from other "modern" Lisps, inspiration from popular ELPA libraries, actual use in Emacs or packages, and users asking for a feature. And again - Compat is there to distribute the new functionality as soon as possible such that it can be used even on old Emacs versions.

@minad @oantolin @jameshowell I don't want to stop you from suggesting it on the proper channels, but it is my experience that a `read-file` function is almost always abused, especially by people with a background in functional programming with lazy evaluation. So for me this is not a theoretical concern, but practical experience that makes me skeptical.

And in general: Something being common is a bad sign that it is the right thing to do, let alone should be encouraged.

@pkal @oantolin @jameshowell I disagree with this perspective, but it is typical for the patronizing view present on emacs-devel. The function is common and has been requested repeatedly, but of course emacs-devel knows better. Even if the function is useful only occasionally, in throw away code, or in custom code in the init.el, it is useful. There is still the docstring if you want to nudge people to reconsider its usage.
@pkal @oantolin @jameshowell I think of Emacs as a practical tool, it is not the purest of the pure languages, or of the Lisps. There is nothing wrong with offering handy utilities, even if their usage should be considered carefully. I see Lisp as part of the (low level) UI of Emacs, which is used by advanced Emacs users when they write their own commands and small extensions.
@pkal @oantolin @jameshowell Regarding ensure-proper-list I challenge you to find places where this function is the right tool - I claim there are none ;) At least for reading files to string, I have seen a few places where such a function would have come handy. I've written it multiple times and I've always ended up with inlining the function body, but the code could have been shorter and more readable.
@minad @oantolin @jameshowell I just pushed the change to package.el that motivated function: https://cgit.git.savannah.gnu.org/cgit/emacs.git/commit/?id=5589d44ce6b. Judge for yourself if you think this is worthwhile or not, we can still undo the change and remove the function.
@pkal @oantolin @jameshowell Yes, this is a valid use case. However I think the addition of ensure-proper-list is only justified if there is more than a single callsite. My usual rule of thumb is to have at least three callsites. Furthermore I have the feeling that ensure-proper-list checks too much. It would be sufficient to check if the object is a cons cell with non-nil non-cons cdr, but then the name of the function would need to change.

@minad @oantolin @jameshowell I don't think there is anything wrong with the patrons of a language to be patronizing 😉.

But seriously, there is long-term effort put into the language to promote a kind of coherence in the language, where locally unpopular decisions (and mistakes) have to be made from time to time. I agree that Elisp is a practical language, but that doesn't mean to me that it is wrong to have a overall vision of how the language should develop and stand by it.

@pkal @oantolin @jameshowell I think there is something wrong, in particular if the arguments are nonsense, e.g., the lazy evaluation argument. I had some real world use cases for a slurp function, many programming languages come with a slurp function, and these reasons seem like justification enough.
@pkal @oantolin @jameshowell Also a slurp function is a tiny addition, this is not about overall language design or an overall vision. It is just a tiny, handy utility and it should be treated and evaluated as such. Btw, generally I think that the advantage of Lisps is that you don't need much of an overall vision. You can bootstrap quickly with few primitives and hack along. ;)
@minad @oantolin @jameshowell The addition itself would be tiny, but that is not what I am thinking about. For me a large part of Elisp is that buffers are preferred over strings (in certain cases). IMO the addition of a slurp would disturb _this_ part of the design that emphasizes buffer operations, and where it is "intentional" that if you want to read the file contents into a string you have to perform a little with-temp-buffer dance as a reminder that that you should rethink something.
@minad @oantolin @jameshowell And to be clear, this is my opinion, which makes it seem like I care about it more than I do. If you really think this is a good addition, please prepare a patch and send it to bug-gnu-emacs where a proper discussion can be had, without uncomfortable character limits.
@pkal @oantolin @jameshowell Of course it doesn't make sense to send such a slurp patch to the Emacs tracker, since the Emacs maintainers share your opinion about not disturbing the emphasis of buffer operations. Buf if we forget the slurp function, I still believe that there are other useful utilities inside dash, f.el, s.el, and it may make sense to scan them and find popular utilities which could be adopted. As I said, I had hopes that this would happen a bit more often thanks to Compat.

@minad @oantolin @jameshowell If you think you have a good argument for slurp, as you appear to think you have (which is part of the reason I am not too enthusiastic about having this discussion, especially here), then it is at least worth a try. If you just don't want to submit the bug and have the discussion in your CCs, I can submit the patch and mention your arguments, even if I don't believe in them myself.

I would also be interested what other functionality from s/f/... you like.

@pkal @oantolin @jameshowell I see this point if you want to prevent string usage at all costs. But Elisp is not a coherent language, I see it more pragmatically, so so I think compromises are acceptable. I disagree that it is good design if users have to perform a dance for simple operations, and I think you do neither, otherwise you wouldn't add simple functions like ensure-proper-list.
@minad My point isn't that Elisp should be a language of inconvenience, it is that I think there is a value in slurping specifically being _slightly_ inconvenient as a deterrent.
@pkal Sure. I've repeatedly hit the inconvenience of not having a slurp function readily available in reasonable scenarios, and I am not the only one I guess. So in this respect Elisp is a language of inconvenience. Also I don't need a deterrent, since I am well aware of the implications when handling large strings. There are some other places where Elisp seems inconvenient in comparison to other languages, e.g., anonymous functions, but I have not made up my mind about llama yet.
@minad I don't think the inference (slurp is inconvenient so the language as such is inconvenient) is valid. Also you are not the people one has in mind to deter, but rather people who are not familiar with Elisp's buffer interface and would instinctively look for a "read-file-into-string" function. If you know what you are doing, it is easy to write it yourself, the act of doing so is like a little "I know what I am doing" checkbox.
@pkal I said that Elisp is inconvenient with respect to slurping, and it is. There are other places too where Elisp is inconveniently verbose like anonymous functions. I think Elisp could serve users who want a slurp function better. But as you say, other arguments carry more weight in the current "vision".
@minad I am not sure you understood the lazy evaluation argument, and perhaps that is the wrong perspective, because I don't think it is nonsense. I am talking about examples like these: https://github.com/jacktasia/dumb-jump/commit/194bfdbe305e21d8e6d29893d449686c85273207#diff-2d6bffbcb3226a3c0ac7c14edb777254463f9cd4a05025e8fb1eb87d032cbf17L2337-L2361. The "lazy evaluation" point is more related to instances like https://github.com/jacktasia/dumb-jump/commit/194bfdbe305e21d8e6d29893d449686c85273207#diff-2d6bffbcb3226a3c0ac7c14edb777254463f9cd4a05025e8fb1eb87d032cbf17R1926-R1928. first . filter is the same as find given lazy evaluation, whereas it is is much more expensive when evaluating strictly.
@pkal Yes, sure. But we are talking about a strict function and strict strings. I don't think developers must be protected from getting the complexities wrong.
@pkal @oantolin @jameshowell I agree that improving the overall coherence is good, but it is ridiculous to claim that there is an overall vision. There is none, maybe except that Elisp should not copy CL. Look for instance at cond* which was added because RMS doesn't like pcase, and because of some anti-pcase/anti-CL argumentation. Pcase is a solid concept, present in other languages with pattern matching, and yet people on emacs-devel claimed that it is hard to understand and should be avoided.

@minad @oantolin @jameshowell I disagree and feel that this undermines the decades of effort that have been put into working towards trying to make (at least the core of Emacs) more coherent and consistent -- which is inherently an uphill battle in a distributed and collaborative projects of this age using a malleable system.

FWIW, cond* was mainly a RMS-driven project, not emacs-devel in general, that is now mostly interesting due to the things it does that pcase doesn't, like bind-and*.

@pkal @oantolin @jameshowell What undermines what? I stand by the opinion that there is no overall vision. Similar statements have even been made by former Emacs maintainers. The point is that Elisp can work perfectly well as test bed for new experiments, like cond*, pushed forward by different people with different preferences and different visions. But these experiments don't improve coherence. You cannot have both, coherence and new competing language elements, e.g., cond* vs pcase.
@pkal @oantolin @jameshowell My point is that Elisp is changing over time, depending on who maintains it, who adds or removes new features, and there has not necessarily been an agreement on all features and all design decisions, and this somewhat implies that there is no overall vision. For example Gerd has the vision to add CL features, like symbol packages, then there was the lexical binding change, which was strongly opposed by some people.
@pkal @oantolin @jameshowell To be clear, I like coherence improvements, but the downsides and cost should be taken into account. I prefer preserving compatibility over enforcing coherence, and I like new pragmatic utilities. I have made my peace with Emacs being a pragmatic, and not a pure system, and I hope it stays that way. Also there is no single answer, and each change must evaluated separately.
@minad Coherence doesn't have to mean absence of competition, these are different matters? If there is a vision of how Elisp "looks and feels", I'd argue it contains both pcase and cond*, which just happen to both be implemented with different strengths and weaknesses. This is not a vision that determines exactly how everything should look like from first principle, which might be the root of the confusion here.
@pkal My point is that it will not lead to more coherence if one introduces different language elements which essentially do the same, or which serve a similar purpose. But maybe one or the other is lacking such that both have their justification. I am not convinced of that, given that cond* was designed because of resentments against pcase.
@minad I love it when a docstring doesn't just tell you what a function is for, but also gives you some advice on when to use it and when not to! @pkal @jameshowell

@minad @oantolin Thank you for these sentiments. I teach biochemistry, and I cook meals and wash dishes for my family. When I'm not doing those things, my hobby is to bork around with parentheses until I can giggle that I got my email client JUST HOW I WANT IT hee hee hee hee hee!

Respect to @pkal and everybody on emacs-devel. But in the docstring, think of the schmucks who just like to bork around.

@jameshowell oh, I think advice about when to use a function isn't useful only for us non-programmers. The advice can stem from knowledge of internals that you want to save everyone from having to learn. @minad @pkal
@pkal Instead of protecting these poor pure functional programmers from themselves, I think we should treat Emacs users as adults. 🙄@minad @jameshowell
@oantolin @pkal @jameshowell Yes and the example of lazy IO is not a realistic reason for skepticism and not related to Elisp. There are not many Haskell users actually. I've also written a bit of Haskell a few years ago with some packages on Hackage. But if we are talking about edge cases, I have a special case where I think a file to string function would be legitimate. A while ago I wanted to read small files from the /proc fs and use the content either directly, or convert it to a number.
@oantolin @pkal @jameshowell When reading such small files, only a small string is going to be allocated, and we will likely end up with less pressure on the Elisp runtime system and garbage collector. In contrast, buffers are relatively fat objects in Elisp, and their creation leads to manipulation of the buffer list and hook calls. To be fair, we have with-work-buffer to mitigate the costs.

@oantolin @minad @jameshowell I don't mean it as much "protecting them from themselves", but more "disincentivizing unidiomatic code for the sake of the users of packages".

But perhaps I am wrong, and something like this will be accepted. I just know that some of the worst Elisp code I remember reading refused to use buffers which biases me.

@pkal @oantolin @minad @jameshowell IMHO, the biggest sin is using eval + shell-command in the modeline.

@pkal @oantolin @minad @jameshowell ... at least for performance. Otherwise, the worst thing I saw is (let ((major-mode ...)) ...)

P.S. Ironically, Org mode does it. But that's (let ((major-mode 'org-mode)) ...), so we at least control the internals.