Convolute Lisp S-Expressions With Smartparens
Discover the magic of convoluting s-expressions when editing Lisp code. Once it clicks, it could save you many hundreds of keystrokes.
“Convoluting a lisp s-expression” sounds like computer science ivory tower bollocks, but after I actually learned what it was and how to use it, I’m seeing it pop up quite regularly when writing lisp. Let me show you the magic; I promise every time you get to use it, you’ll feel like a king!
Let’s say you’ve been writing some code in a stream-of-consciousness way. First you bind some local variables using let
and call do-something
:
(let ((x 1) (y 2)) (do-something x y))
Oh, but now you only want to do-something
when
condition-p
is true:
(let ((x 1) (y 2)) (when condition-p (do-something x y)))
OK, sweet!
…but wait, if condition-p
is false then the let
bindings are useless! You can see that the when
should have wrapped around the let
instead of just the body inside let
. Now you have to tediously flip everything around by hand, right?
No! sp-convolute-sexp
to the rescue 🦸♂️
Put your point at the opening parenthesis of (do-something x y)
and run M-x sp-convolute-sexp
and in one fell swoop…
(when condition-p (let ((x 1) (y 2)) (do-something x y)))
So many keystrokes saved, right? Never mind feeling like a king — you’ll feel like a wizard 🧙
A More Abstract Explanation
It doesn’t matter how many s-expressions there are in the surrounding code — they usually won’t get in the way. You just have to keep a few specific rules in mind. Let’s break it down in an abstract example, with no distracting “reality” or whatever that is 🤓
(outer x y z (inner a b c d e f) j k l)
If you convolute around any of the s-expressions a b c d e f
then the s-expression surrounding outer
will be swapped with the s-expression surrounding inner
and all the s-expressions immediately preceding your cursor will be chopped off and moved alongside inner
.
So when we simply convolute around a
we’ll get:
(inner (outer x y z a b c d e f j k l))
But had we convoluted around d
then we would have gotten:
(inner a b c (outer x y z d e f j k l))
Keybinding Tips
I like to map it to a binding that includes the %
character, since it visually reminds me of swapping something above to below and reinforces that pattern in my head so I remember to use it.
I’ve included it in my Smartparens hydra, which I’ve been working on improving as I write more and more lisp. But that’s a future post.