Endless Parentheses

Concise ramblings on Emacs productivity.

New in Emacs 25.1: More flow control macros

One of my personal favorite new additions to Emacs 25 is, in fact, completely invisible to most users. The new macros if-let and when-let, although simple in purpose, are a delight to use and are frequently finding their way into my code. The other two additions, thread-first and thread-last, are a bit more specific, and take a bit getting-used-to if you’ve never seen them before.

The two *-let macros are easier to explain by just expanding them. All they do is summarize a very common situation for lisp programmers. Instead of writing this:

(let ((x (some-func)))
  (if x

you can write this:

(if-let ((x (some-func)))

Seeing this example it shouldn’t be hard to understand when-let as well.

As you can see, it’s not exactly a revolution — you barely even save any typing. But that extra level of nesting you save, along with an added bit of clarity, for some reason makes me smile every time I get to use them.

The threading macros are something you see all the time in Clojure (under a shorter name) but are probably not going to be as common in Emacs-Lisp — your average Elisp code is far less functional than Clojure code. Still, they’re good to know as they can be a huge help in some situations. Explaining again by example:

;; First
(thread-first x
  (concat y)
  (format z))
;; Expands to this:
(format (concat x y) z)

;; Last
(thread-last x
  (concat y)
  (format z))
;; Expands to this:
(format z (concat y x))

If the examples above don’t look too useful to you, that’s because they’re not. thread-first and thread-last are relatively short names by Elisp standards, but they’re still long enough that you’re almost always going to be typing more, instead of less, when you use these macros. Instead of saving space, their value lies in improving readability of some rather extreme scenarios (which are all too common in Elisp). For instance, which one of the forms below would you find easier to read?

;; This?
(thread-last some-string
  (replace-regexp-in-string "regexp-1" "replace-1")
  (replace-regexp-in-string "regexp-2" "replace-2")
  (replace-regexp-in-string "regexp-3" "replace-3")
  (replace-regexp-in-string "regexp-4" "replace-4")
  (replace-regexp-in-string "regexp-5" "replace-5"))
;; Or this?
 "regexp-5" "replace-5"
  "regexp-4" "replace-4"
   "regexp-3" "replace-3"
    "regexp-2" "replace-2"
     "regexp-1" "replace-1" some-string)))))

Finally, for those of you who use my speed-of-thought-lisp package, it already has abbrevs for these macros under il, wl, tf, and tl.

Tags: emacs-25, emacs

Say thanks on Gratipay
comments powered by Disqus