It probably happens to anyone coming from a more mainstream, imperative language: you get your first look at lisp and think, “What kind of idiot designed this language? How could anyone in their right mind enjoy looking at, let alone typing, all those parentheses?”
It is not terribly uncommon to see a line of lisp that ends in six or seven closing parentheses. If you have never seen what lisp looks like, here is an example of a short function definition:
(defun duplicate-current-line-or-region (arg) "Duplicates the current line or region ARG times. If there's no region, the current line will be duplicated. However, if there's a region, all lines that region covers will be duplicated." (interactive "p") (let (beg end (origin (point))) (if (and mark-active (> (point) (mark))) (exchange-point-and-mark)) (setq beg (line-beginning-position)) (if mark-active (exchange-point-and-mark)) (setq end (line-end-position)) (let ((region (buffer-substring-no-properties beg end))) (dotimes (i arg) (goto-char end) (newline) (insert region) (setq end (point))) (goto-char (+ origin (* (length region) arg) arg)))))
As some lispers have said before, and as I have started to experience, when writing and reading lisp code, the parentheses begin to fade into the background. What you really see when looking at the code is the indentation. Lisp has very specific indentation rules, and as you become familiar with lisp, it becomes very easy to spot when something is not nested correctly. In the function above, there are two “let” statements, and the lines below them are indented by two spaces. Likewise there are two “if” statements, and the lines below them are indented by four spaces. If I had nested my s-expressions incorrectly, the automatic indentation would look wrong, and it would be easy to spot the error without having to count parentheses.
A couple of very key tools make coding in lisp a dream, instead of a nightmare. One tool that goes almost without saying is a good lisp-editing mode in a good text editor. And by “good,” I mean that it accurately and consistently follows lisp’s indentation rules.
The second tool is one that I have found absolutely essential to writing in lisp. It is called “ParEdit.” ParEdit is like a very (VERY) strict version of an “electric parentheses” mode – it automatically types a closing ”)” when you type an opening “(.“ The strict part is that it subtly changes all of the deletion commands as well, and so it is actually impossible to have unbalanced parentheses.
As you might imagine, that could be frustrating. And like lisp itself, when I first tried it, I hated it. It would not let me delete anything! It was aggravating to the point I was cursing at my computer. But, of course, that is because I did not understand it. And again, like lisp itself, since I made the time to dive deeply into it (“drink the ParEdit Kool-Aid,” so to speak), I now understand that it is the only way to edit lisp code properly and efficiently. I will never write lisp without it again.
To give a little taste, one of the very handy ParEdit commands is called “slurp,” and its function is to absorb the next balanced set of parentheses (called an s-expression) into the current set. This will be easier to illustrate than explain in words (the ”|” character will represent the cursor).
Let’s say I have a line of code that does something once:
(defun myfunction () (do-something-once))|
Now I want to wrap it in a loop. I move the cursor up and as I type “(while t” ParEdit has already inserted the closing ”)” after my cursor, so I don’t have to worry about that:
(defun myfunction () (while t|) (do-something-once))
Now for the magic of ParEdit. I type the keyboard sequence for “slurp” (in my case “Control+)”) and now my code looks like this:
(defun myfunction () (while t| (do-something-once)))
Notice how, besides reorganizing the parentheses, ParEdit has also properly re-indented the code. When you get used to this, it is a beautiful, beautiful thing. The “slurp” command also has an inverse, which is comically called “barf;” and they both have a leftwards versions, too, (instead of slurping the s-expression to the right/down, you slurp the one to the left/up). This makes re-arranging your code extremely fluid and simple, as in the example above where we wrapped a piece of code in a loop. Note that this works as expected with deeply-nested s-expressions as well.
The final advantage of note is a mental one. The most powerful thing about lisp is that it is composed entirely of s-expressions. In most languages, there is different syntax for things like “if” conditionals vs. function invocation vs. function definition. In lisp, it is all just parentheses-delimited s-expressions. It makes you think about code in a different way. It is all just a big nested tree. Of course, this is what most languages are under the hood (they call it the abstract syntax tree). In order to understand lisp fully, you need to have this mental shift take place, and ParEdit facilitates that by allowing you to directly operate on the s-expression tree with its specialized commands. So, I would posit, not only does ParEdit make you faster at coding, it also facilitates and increases your understanding of the code you are working with.
ParEdit was originally written as an emacs package1, but there is a version for vim2 and one for sublime text3. All of these versions are open source. There may be other implementations available as well.
Have you or do you plan to use ParEdit?
1 ParEdit emacs package
2 ParEdit paredit.vim
3 ParEdit sublime-paredit