2016-04-24 The conditional-save-excursion macro

One day I was writing a library of helper functions for writing messages (I will definitely share them in due time!). One of the things there is (semi-)automatic insertion of salutations and closings. You know, all this “Dear Sir/Sincerely yours” stuff which is basically meaningless (and harmful in term of time spent – on both sides of the wire!) in an email, but many people expect it anyway. I am now in the process of moving to shorter email messages. This is one of the signatures I might recommend – based on http://two.sentenc.es

Q: Why is this email so extremely laconic and has no courtesies?
A: Because our time is more valuable than our egos.
http://two.sentenc.es

And one of my goals is to insert such a signature automatically, with the proper sentence count.

By the way, don’t get me wrong. I’m all for “slow life”, and not succumbing to the rat race and the “do everything as fast as you can so you can take on even more insane amount of work”. But since the time I’ve been trying to measure time I spend on various things (and optimize it), I noticed that email handling takes up a considerable amount of my time. And even if automating writing emails seems not really worth the time spent on coding it, the linked table does not take into account two factors: first, if I share my code with others, the savings are basically multiplied by the number of people using the code (without the overhead of them coding it!). Then, it is not only the time which is involved: even if do not really save time (because of all time spent on coding), I have some fun doing it, I constantly larn something along the way, I share what I have learned and coded, and I remove the frustrating part from email writing.

Coming back. When I launch an Emacs command to insert or change the salutation, I usually wouldn’t want to get distracted, so I don’t want the point to move. The save-excursion form is made for such a purpose. OTOH, sometimes I might want to edit the salutation (e.g., add a name to it). In such a case, I do not want save-excursion.

Incidentally, this leads to another kind of time-saver: Lisp macros. What I wanted was a “special form” conditional-save-excursion, with an additional (Boolean) parameter determining whether we should actually bring the point back to where it was or not.

In most languages, you simply cannot do that: you may write functions, yes, but you can’t write your own piece of syntax, or write your own conditional construct (see the first exercise here) with them.

In Lisp, there is one powerful tool: macros. (They are conceptually similar to C preprocessor macros, only that they are better, because instead of some crippled language lacking reasonable constructs you get a full-fledged Lisp.) Macros allow for delayed execution of Lisp code (among other things), which is exactly what we need here.

(defmacro conditional-save-excursion (arg &rest body)
  "Wrap BODY in `save-excursion', but only if ARG is non-nil."
  (declare (indent 1) (debug t))
  `(if ,arg
       (save-excursion ,@body)
     (push-mark)
     ,@body))

And now I can say e.g. this.

(defun insert-foo-at-the-bob (leave-point-at-foo)
  "Insert the string \"foo\" at the beginning of buffer.
With prefix argument, leave the point there."
  (interactive "P")
  (conditional-save-excursion
      (not leave-point-at-foo)
    (goto-char (point-min))
    (insert "foo")))

Try it out!

Notice also the declare form in our macro definition. The (indent 1) clause instructs Emacs to indent instances of conditional-save-excursion properly. The 1 means that there is one “special” argument of it, and then the &rest follows. The (debug t) is meant for Edebug. It means that every argument to this macro will be expanded (and hence should be instrumented for edebugging). (It can get pretty complicated for more sophisticated macros; check the manual for details!) Neither of these two forms are necessary for the program to work well, but both help the programmer.

As a last thing I’d like to mention that a very detailed explanation of the above macro – including a step-by-step explanation of how Elisp macros work, and also a short discussion of macros vs. functions – will be part of my upcoming Emacs book. Stay tuned!

CategoryBlog, CategoryEnglish, CategoryEmacs, CategoryEmacsBook