It is not hard to edit Lisp code

Target audience of this article is

  • those who are a bit curious about Lisp
  • beginners of Lisp
  • Dan

The goal of this post is to convince you that you can edit or write Lisp code with ease. In particular, to convince you that in the end managing all those parentheses is no problem at all, that is, you don’t count parens. This post contains

  • editor-agnostic tips on how to edit Lisp code
  • and some tips for Emacs users in particular (which you should of course skip if you use other editors or IDEs)

Readers are assumed to have read the previous article: It is not hard to read Lisp code.

Many tips in this post were already expressed before in some places in some form or another, but I thought I would make a single post containing all tips organized in a particular way, and with examples and exercises. Reading this post, especially if you do all exercises, which you should, is going to take a long time, so at the end of this post, let’s watch some video of dancing chickens to reward ourselves.

1. Intro

Dan: “Eve, I heard you can write Lisp code. Is that true?”
Eve: “Yes. Why?”
Dan: “Do you want to apply to my department? The department needs someone who can do Lisp. Need to modify some small scripts written in Lisp, left by someone who left to become a wrestler. This town now has only two programmers, you and me, and I don’t Lisp.”
Eve: “You can learn Lisp.”
Dan: “I tried, but I find that editing Lisp code is too difficult compared to other languages. Maybe Lisp is not for everybody? And I’m just an amateur programmer.”
Eve: “Trust me, you can become one of those who can edit Lisp code seamlessly. I’ll tell you how.”
Dan: “Will it take months for me to become such? Please don’t tell me to just use Emacs”
Eve: “Whatever decent editor you choose, you and your text editor together start a journey. A journey that is much shorter than you expect. This journey passes through three distinct and recognizable phases1, and these are:”

  1. the “no red tape” phase
  2. the “red tape” phase
  3. the “automation” phase

Dan: “Abbreviated to NRA?”
Eve: “Maybe. Anyway, good news is, you are already at the first phase. The first phase works as a good motivator for the second phase. And the second phase motivates you to get to the third phase. You may be able to go through these phases within just one day.”

Just like cooking rice on the stove, something that looks hard at first, but actually easy if you do it in a particular way.

2. Which editor or IDE to use

If you have not decided, I heard that PLT Scheme and LispWorks are good.

In the long term, any editor or IDE that comes with some plug-ins ecosystem is bound to evolve to acquire every possible feature for helping Lisp coding. (If you miss a feature, be excited, you will be the author of an awesome future plug-in implementing the missing feature. Find a work around for now, but still mark a date on your calendar, one year from now, the start date of your plug-in development.) Also, it would be helpful (especially in the third phase) if the editor of your choice allows you to define macros or commands of your own in programmable manner.

What about Vim users? Vim is not my area of expertise, but if you are a Vim user, read this thread. In short, some people made a plugin for you.

The rest of this post assumes that you are using some text editor that is somewhat programmable or a Lisp-specific IDE.

3. The three phases

In the first phase, the “no red tape” phase, you edit Lisp code in an unconstrained way and you have some ways of dealing with mismatched parentheses you sometimes accidentally introduce during this phase. This leads to the second phase, the “red tape” phase, where you edit code under a set of rules. The rules are designed to prevent mismatched parentheses and to promote structure-based editing. But you might find editing in this phase to be cumbersome or tedious. That is why you get to the third phase where you automate things as much as possible. After that, editing per se should be almost effortless, and then what you do is the same as in other languages: read others code, write some code, practice, ask what are good practices, etc, that is, there is more to writing code other than just effortless text editing, but you and Dan already know that.

Before we get to tips for the first phase, let’s go through some tips common to all phases first.

4. Always indent.

Recall (from the previous article) that readability mostly comes from indentation and that there is only one correct indentation style, namely, the indentation that your text editor suggests. Find a keyboard shortcut or a button that correctly indents the selected code.

Suppose you have the following code:

(print (+ 1
          2
          3))
(print "blah")

(defun my-hi ()
  (dotimes (_ 2)))

Let’s say you want to move the two print forms into the dotimes form, so that the resulting code (if correctly indented) is:

(defun my-hi ()
  (dotimes (_ 2)
    (print (+ 1
              2
              3))
    (print "blah")))

Without correct indentation, if you just cut the two print froms and paste them into the dotimes form, you just get:

(defun my-hi ()
  (dotimes (_ 2)
    (print (+ 1
          2
          3))
(print "blah")))

which is badly indented. You must always reindent after you paste any code, using the appropriate keyboard shortcut or button.

While editing Lisp code, always make sure to keep the code correctly indented after every edit (or just after every edit that can mess up indentation, such as pasting). Adopting this obsessive habit will help you in two ways:

  • code is always kept readable (it is hard to edit less-readable code, more so for beginners)
  • easier to spot missing open parens (a.k.a. opening parentheses) or close parens (a.k.a. closing parentheses) (as is demonstrated with the following exercise)

Exercise 10. Try deleting the third line from the following code (thereby making it an invalid code because missing open parens) and then reindent the whole code and see what happens. Alternatively, try deleting the 5th line and reindent and see.

(defun my-hi ()
  (dotimes (_ 2)
    (print (+ 1
              2
              3))
    (print "blah")))

If this habit seems tedious, find a way to automate some of it in your editor. It is highly likely that automating this is already a feature or there is a relevant plug-in for your editor, otherwise, mark a date on your calendar.

4.1. Emacs note

Some notes for Emacs users.

On an Emacs Lisp buffer (a buffer you get by opening a file with extension .el), the key C-j runs the command newline-and-indent which inserts a newline and then indent according major mode (so, pressing C-j is the same as pressing RET TAB). On the scratch buffer, the same key does a different thing. Other Emacs notes in this post assumes that you are coding on an Emacs Lisp buffer, not on the scratch buffer. Speaking of which, you might want to see how to enable Unicode encoding, lexical scope, and CL functions in an Emacs Lisp file. I will also assume that you are using the latest stable version of GNU Emacs.

Also check out M-j which runs indent-new-comment-line which seems to do the same thing as C-j except some slight difference when you run this command within comments.

Recall the two keys for indenting in Emacs: TAB and C-M-\

TAB runs the command indent-for-tab-command which in Emacs Lisp buffers simply indent the current line or region according to the elisp indentation standard. C-M-\ runs the command indent-region which does exactly what you expect from its name: it indents the region according to the standard.

It is a good habit to always press C-M-\ right after you paste some expressions, and it will correctly indent the pasted expressions, even if you have not manually selected the region before pressing C-M-\ (unlike TAB).

For the purpose of this post, the following terms are interchangeable:

  • “expression” or “symbolic expression”
  • “balanced expression” in the Emacs manual and documentation of commands
  • “sexp” in some Emacs command names

Some other indentation tips are scattered across other Emacs notes in this post.

5. Evaluate!

Lisp happens to be one of those dynamic languages. You may have heard of pros and cons of such languages. Do exploit the pros such as rapid prototyping or quicker “code, test, code again, test again” loop during Lisp coding. Remember that you can evaluate the whole code (the whole file), or just some part of the code, or even just one defun form that you just modified a bit. If this paragraph does not seem to make sense, watch this impressive demo video and you’ll get it.

In your text editor, find a way to evaluate some selected part of code, or find a plug-in for it.

5.1. Emacs note

Open an Emacs Lisp file, empty or not. Notice the “Emacs-Lisp” menu in the menu bar and see that there is a menu item for evaluating a region (selected region), and another for evaluating the last expression (the last expression before point). Did you know that you can use C-h k even for menu items and toolbar items? Use C-h k to read documentations for the two commands bound to the two menu items.

Visit any Emacs Lisp buffer and then press C-h m to see if there are any keys that hook your interest. Let’s see. On vanilla Emacs, I see:

C-M-i           completion-at-point
C-M-q           indent-pp-sexp
C-M-x           eval-defun
...

C-M-i is what you can press right after you type def and then Emacs will say to you “Possible completions are defadvice, defalias, default, …. Would you like to choose one?”.

C-M-q is for when you have some badly indented code like so:

(defun my-hi ()
  (dotimes (_ 2)
(print (+ 1
          2
          3))
(print "blah")))

Assuming that point is at anywhere within the above snippet, you can press C-M-u multiple times until point is at the start of the defun form, and then you can press C-M-q to fix indentation of the defun form. It seems that pressing C-M-q is the same as pressing C-M-SPC C-M-\

C-M-x runs eval-defun which evaluates the top-level form containing point, or after point. The command is named eval-defun probably because a top-level form is often called a defun (at least within the Emacs documentation), regardless of whether it is actually a defun form or not.

Here is how you might edit some Emacs Lisp code. The workflow is like this. Let’s say you have some code in your init file that you copied from somewhere else, maybe from EmacsWiki. One day, you decide you want some slightly different behavior from the copied code. So you modify some defuns in the code, and then you evaluate them right there, and then you can test the change you just made. If the test indicates that something is wrong, you modify the code again. The cycle continues until you get it all right. In each cycle, you may or may not need to restart Emacs, and most of the times you don’t.

6. One liners

Beginners should stay away from writing one-liners like:

(defun my-hi () (dotimes (_ 2) (print (+ 1 2 3)) (print "blah")))

because:

  • editing a long one-liner is hard, more so for beginners, and
  • reading a one-liner is hard.

Let’s say you ended up writing some very long one liner (this can happen even if you didn’t mean to). What can you do after the fact? You can break the one line into multiple lines, and then, as always, you reindent the code.

You might say, “exactly how should I break this line into multiple lines? I mean, where in the original line should I insert newlines?” There isn’t one answer, but as you read more and more Lisp code, you will get a feel for where to put newlines.

7. Breaking lines

Whenever you are breaking a line, don’t forget to indent all the way. For example, consider the following code:

(let ((n (frobbotz))) (display (+ n 1)
                               port))

Let’s say you want to break the first line into two lines by breaking at the start of the display form. If you just insert a newline there, you get:

(let ((n (frobbotz)))
(display (+ n 1)
                               port))

which is badly indented, and then if you press some button to indent the current line (the second line), you get:

(let ((n (frobbotz)))
  (display (+ n 1)
                               port))

which is still badly indented. You need to correctly indent the third line as well, to get:

(let ((n (frobbotz)))
  (display (+ n 1)
           port))

In your text editor, there should be a command for breaking a line while also automatically deleting trailing whitespaces (for example, you might want to delete the trailing whitespace that might be at the end of the first line in the new code).

As for indenting all the way, a good alternative to “Correctly indent the current line. Move to next line. Correctly indent the current line. Move to next line … Done.” is to simply press the button for selecting the expression (following the text cursor), and then press the button for correctly indenting the selected expression.

7.1. Emacs note

Suppose again you have this code:

(let ((n (frobbotz))) (display (+ n 1)
                               port))

On vanilla Emacs, move to the start of the display form, and then press: C-j C-M-SPC C-M-\ and see what happens. Alternatively, you could have pressed C-j C-M-q instead, which is shorter. Notice that C-j deletes the trailing whitespace automatically. (M-j deletes the trailing whitespace too.) Consequently, before you press C-j, you could have put point at anywhere between the end of ((n (frobbotz))) and the start of the display form2.

If you use paredit, C-j is bound to the command paredit-newline so that you don’t even have to press C-M-SPC C-M-\ or C-M-q afterward. Paredit is amazing like that.

8. Joining lines

Sometimes you might want to do the reverse of breaking a line, that is, you might want to join two lines into one. Your text editor is likely to have a command for exactly that. Such command should, automatically in addition, combine many whitespaces into one. In other words, when you have the following code:

(display (+ n 1)
         port)

and then when you invoke the command for joining the two lines, you should get:

(display (+ n 1) port)

rather than this:

(display (+ n 1)         port)

Find that command in your text editor. (Emacs note: See C-h f join-line)

Exercise 20. Invoke the command three times to change from this:

(display (+ n
            1)
         (+ n
            1))

to this:

(display (+ n 1) (+ n 1))

(Emacs note: you need to start from the last line, not the first line.)

Exercise 30. Now do the reverse. Split the line back into four lines.

Exercise 40. Break the following one line into many lines, however way you want. Then join to single line again. Then break again, but this time, break in a different way.

(setq exp (read-string (format "%s expansion for \"%s\": " type name) nil nil nil t))

9. Placement of close parens

Suppose you have this Common Lisp code:

(defvar *persons*
  (list (make-person "fred")
        (make-person "jane")
        (make-person "susan")))

If you want to change that to the following code, you only have to invoke the command for breaking a line once.

(defvar *persons*
  (list (make-person "fred")
        (make-person "jane")
        (make-person "susan")
        ))

The latter style makes it easier to append a new make-person form (as the new last item). (Nevertheless, it is not that hard to append a new item in the former style. We’ll get to that.)

Some beginners go too far with this and make lots of lone trailing parens on their own lines, like so:

(defun my-clone-indirect-buffer-other-window (newname display-flag &optional norecord)
  "Like `clone-indirect-buffer' but display in another window."
  (interactive
   (progn
     (if (get major-mode 'no-clone-indirect)
         (error "Cannot indirectly clone a buffer in %s mode" mode-name)
       )
     (list (if current-prefix-arg
               (read-buffer "Name of indirect buffer: " (current-buffer))
             )
           t)
     )
   )
  (let ((pop-up-windows t))
    (clone-indirect-buffer newname display-flag norecord)
    )
  )

Exercise 50. Invoke the command for joining lines multiple times to change that to this code:

(defun my-clone-indirect-buffer-other-window (newname display-flag &optional norecord)
  "Like `clone-indirect-buffer' but display in another window."
  (interactive
   (progn
     (if (get major-mode 'no-clone-indirect)
         (error "Cannot indirectly clone a buffer in %s mode" mode-name))
     (list (if current-prefix-arg
               (read-buffer "Name of indirect buffer: " (current-buffer)))
           t)))
  (let ((pop-up-windows t))
    (clone-indirect-buffer newname display-flag norecord)))

You might want to read this thread from which above code is also taken.

Some things I want to point out before we move on to the next section:

  • Putting close parens together (rather than writing close parens on their own lines) is the convention.
  • We’ll get to why following this convention is not hard.
  • There are some cases where putting close parens separately can be OK. (See this thread and Rainer Joswig’s answer.)

Some people put some close parens separately to indicate unfinished code, like so:

(defun my-blah ()
  (let ((b 2))
    (why)
    (did)
    (the)
    (chicken)
    ))

and then when they are done writing the let form, they join close parens together to indicate that writing the let form is done, like so:

(defun my-blah ()
  (let ((b 2))
    (why)
    (did)
    (the)
    (chicken)
    (cross)
    (the)
    (road)))

Maybe that can be a useful habit for you to adopt as well, if you decide you like it.

I just realized that I have never seen a chicken cross a road. If I ever get into poultry farming, I should put a realistic miniature road in my farm, and make it a tourist attraction for those who want to see a chicken cross a road at least once in their lifetime.

10. The “No red tape” phase

There are many ways of dealing with occurrence of mismatched parentheses in this phase. One of them is to simply check for syntax errors more often. Maybe press the “compile” or “run” button often, if any, or the “is there a missing paren” button often. Recall that you can evaluate just one defun you just edited instead of evaluating the whole code.

In your editor, there should be a command for locating missing parens. If not, evaluating or compiling is a good enough substitute because it would at least report existence of missing parens if any. If something like real time syntax check is provided, that also helps.

What should you do when real time syntax check told you that you accidentally just introduced some mismatched parens? Simply undo (usually bound to Ctrl+Z) and start over. Example situation: Suppose you have the following code:

(defun my-hi ()
  (dotimes (_ 2)
    (print "hello")
    (print "world"))
  (print "the end"))

Suppose that you want to remove (print "world"), and that you (wrongly) decide to do that by deleting or commenting out the second-to-last line. So you have:

(defun my-hi ()
  (dotimes (_ 2)
    (print "hello")
  (print "the end"))

But then the real time syntax check now tells you that there is a missing paren. Even if real time check isn’t supported in your editor, you would find that out anyway if you invoke the indenting command on the defun. Upon realizing that just deleting the line was a wrong way to remove (print "world"), you can simply undo the deletion of line and start again.

One might ask: is there a tool that automatically fixes missing parens? Probably impossible.

10.1. typing the correct number of closing parentheses

This is something you don’t have to deal with in later phases (second and third phases), but in the “no red tape” phase, you do need help for this from your text editor.

(sqrt (+ (expt (/ ΔL′ (* Sl kL)) 2.0)
         (expt (/ ΔC′ (* Sc kC)) 2.0)
         (expt (/ ΔH′ (* Sh kH)) 2.0)
         (* Rt (/ ΔC′ (* Sc kC)) (/ ΔH′ (* Sh kH)))))

If you were to write above code, how would you figure out how many close parens to type at the end? If your editor has the feature of highlighting the open paren matching the close paren you are typing, that is how you know how many to type, you just type close paren repeatedly at the end until the matching open paren is the one you want to match.

One might ask: can there be a command that just inserts the correct number of close parens? A command like “Insert enough close parens to complete this top-level form” can be made, but in general for non-top-level forms, probably not.

10.2. Emacs note

On vanilla Emacs, every time you type a close paren, the text cursor moves to the matching open paren for a second. Handy in the first phase.

Exercise 55. See what happens if the matching open paren is not visible on screen (for example, when it is somewhere above the displayed lines). (Watch the echo area).

If you enable show-paren-mode (available within vanilla Emacs), whenever point is after a close paren (resp. before an open paren), Emacs shows you the matching open paren (resp. close paren). Have this mode enabled. Useful in all phases.

To check for missing parens in real time, use FlyParens.

You probably know how to undo in Emacs, and how to bind the undo command to C-z without enabling CUA mode, if you don’t want to use CUA mode for some reason.

11. The “red tape” phase

Spend some time trying editing Lisp code in the first phase. If editing Lisp code does not feel cumbersome then, or if it feels only as cumbersome as editing code in other languages, then there is probably no need for you to go to the second phase, and no need to use anything like paredit. But if you are not satisfied with the first phase, or if maintaining balanced parentheses still feels like a chore, then it’s time to move to the second phase, the “red tape” phase.

The first rule of the second phase is this: you will type parens only in pairs (except in comments and strings). In other words, every time you type an open paren, the next action should be to type a close paren, and every time you type a close paren, the previous action should have been typing of an open paren. Typing of () should be considered an atomic operation from now on, in other words, you never do something like typing just an open paren and then doing something other than typing the corresponding close paren.

By following this rule, you never introduce mismatched paren in the first place. But how does one type (foo) without violating the rule? You first type () and then type foo between the two parens.

Exercise 60. Configure your text editor so that there is a convenient keyboard shortcut for typing () (and then placing the text cursor between the two parens).

Emacs note: M-( runs the command insert-parentheses which types ().

How would you type

(let (a b c) (foo))

Let’s make that more interesting. How would you write the following?

(let (a
      b
      c)
  (foo)
  (bar))

One way of writing that is to first write the following (which you now know how to write without violating the rule):

(let)

and then write (a b c) to get:

(let (a b c))

and then

(let (a b c)
  ())

and then

(let (a b c)
  (foo))

and so on. (Recall that you already know how to break (a b c) into multiple lines.)

Another way is to first write:

(foo)
(bar)

and then wrap that with a let form, but how do you do that? If you cannot find a command for that, you can simply cut the two expressions (foo) (bar) into clipboard and then write the following

(let (a b c))

and then break the line to get:

(let (a b c)
  )

and then paste back (foo) (bar) so you get:

(let (a b c)
  (foo)
  (bar))

Exercise 70. Try both ways.

Second rule is that you don’t do anything that results in mismatched parens. For example, deleting or commenting out the second-to-last line in the following code is not allowed, but deleting just the expression (print "world") is OK.

(defun my-hi ()
  (dotimes (_ 2)
    (print "hello")
    (print "world"))
  (print "the end"))

What are some of allowed operations then? Here are some, which I will call “basic operations” from now on:

  1. well formed cut – cutting one or more expressions to the clipboard (As mentioned in the previous article, by “expression”, I mean symbolic expression)
  2. well formed copy
  3. well formed paste – pasting expressions from clipboard (and then indenting correctly)
  4. well formed commenting – commenting out one or more expressions
  5. well formed delete
  6. joining or splitting lines (and then indenting correctly)
  7. typing ()
  8. typing something that does not contain any parens or newlines (except within strings and comments)
  9. opening a blank line or removing one

Notice that the wrapping operation (wrapping with a let form) was done by a well formed cut (of two expressions (foo) (bar)), and then some other basic operations, and then a well formed paste. In fact, notice that the previous exercise was done using only basic operations.

To do well formed cut (or well formed copy) easily, you need to know a convenient way to select expressions. For example, how do you select the bar form and the moo form together in the following code in your text editor?

(+ (foo)
   (bar a
        (b c d))
   (moo a
        (b (c))))

The answer of course is to select from the start of the bar form to the end of the moo form, but where exactly (within those close parens) is the end of the moo form? If your text editor has some features for helping you see which close paren matches which open paren, then you can use those features to pinpoint the ending close paren for the moo form. Your editor should also have separate features or plugins for easy selection of expressions. Find them.

Emacs note: In vanilla Emacs, this is how you would select the bar form and the moo form: move point to anywhere within the bar form, and press C-M-u as many times as necessary (to get to the starting position of the bar form), and then press C-M-SPC as many times as necessary (twice in this case). This is generally how you select one or more expressions.

Notice that the basic operations work on the level of expressions, not on the level of letters, words or lines. Think of each basic operation as an atomic operation. In particular, if you are in the middle of doing some well formed cut and the phone rings, you complete the operation before answering the phone.

You can edit Lisp code using only basic operations, as we’ll see in next sections. The idea of the red tape phase is like the lesson of Vim: “In Vim, the trick is you work mainly with text objects (words, lines, sentences, code blocks, etc), not with letters.” In Lisp editing, the trick is you work with expressions (sexps), not with lines or letters.

Note: in some cases, you might need some kind of clipboard manager (either as a feature within your text editor, or as an external program) if you need use more than one clipboard. These cases will happen.

You should get to know the text cursor movement commands based on expressions (rather than on lines or letters) in your text editor, such as “move the cursor forward passing one expression”, “move up one nesting level.” and so on, because they will come in handy later and because they might be used for selecting expressions.

Emacs note: In vanilla Emacs,

  • the command forward-sexp, for moving past one expression, is bound to <C-M-right> and C-M-f
  • backward-sexp is bound to <C-M-left> and C-M-b
  • backward-up-list, for moving up one level, is bound to <C-M-up> and C-M-u
  • down-list, for moving down one level, is bound to <C-M-down> and C-M-d

12. How would Eve write it

Suppose Eve wrote the following elisp code, which can look highly nested and complicated to beginners.

(defun my-preview-config ()
  (when (<= 900 (x-display-pixel-height))
    (setq preview-scale-function 1.5)
    (when (eq system-type 'windows-nt)
      (let ((winversion
             (when (string-match (rx "nt"
                                     (group (+ digit))
                                     "."
                                     (group (+ digit))
                                     "."
                                     (+ digit)
                                     eos)
                                 system-configuration)
               (list
                (string-to-number (match-string 1 system-configuration))
                (string-to-number (match-string 2 system-configuration))))))
        (when (version-list-<= '(6 2) winversion)
          (setq preview-scale-function 'preview-scale-from-face))))))

How would have Eve wrote that code, using only basic operations? The answer lies in, how do you draw a face? You don’t normally draw a face from left to right like you would with writing a sentence. You draw from outside to inside, that is, from big picture to small detail. First, you draw an outline of a face, like so.

.
.                                    
.                                     
.          |   |    |    |     |   |   
.          |   |    |    |     |   | 
.       +--+---+----+----+-----+---+----+
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       |                               |
.       +-------------------------------+
.

And then you draw outlines for eyes, nose, mouth:

.
.                                    
.                                     
.          |   |    |    |     |   |   
.          |   |    |    |     |   | 
.       +--+---+----+----+-----+---+----+
.       |                               |
.       |    ------        ------\      |
.       |   |      \      /       |     |
.       |   \      |      |       /     |
.       |    -----/        \------      |
.       |                               |
.       |           |      +            |
.       |           +------+            |
.       |                               |
.       |         +------------/        |
.       |         |           /         |
.       |         +-----------          |
.       +-------------------------------+
.

And then maybe you draw some teeth inside the mouth, and fill more details for eyes, and so on. So the big structure is drawn first, and then smaller structures nested inside it, and then even smaller structures inside them.

Eve did not write her code in textual order. In particular, the bunch of close parens at the end are not what Eve wrote last (if she did, she would be violating the rules of the red tape phase). Rather, she drew her code in big-to-small order (that is, outer-to-inner order). Before explaining how exactly she did it, let me explain what the code does: the code defines a function named my-preview-config that does the following (when called):

  1. If the screen height is bigger than or equal to 900, set the variable preview-scale-function to the number 1.5.
  2. But if the system is Windows 8+ in addition, set the variable preview-scale-function to the symbol preview-scale-from-face.

This is how she would write the code. She first write this (which you know is possible using only basic operations):

(defun my-preview-config ()
  )

That is like drawing just the outline of a face. Next, she edit further to get:

(defun my-preview-config ()
  (when --BIG-HEIGHT--
    --BLAH--))

She’s using place-holders like --BLAH-IN-CAPITAL-LETTERS-- to indicate parts not yet written, i.e., parts to be written later. Next, she searches through the elisp reference to find what function to call to get screen height. She learns that it’s x-display-pixel-height, so she edit further to get:

(defun my-preview-config ()
  (when (<= 900 (x-display-pixel-height))
    --BLAH--))

Next, she replaces --BLAH-- to get:

(defun my-preview-config ()
  (when (<= 900 (x-display-pixel-height))
    (setq preview-scale-function 1.5)
    (when (eq system-type 'windows-nt)
      --)))

The expression (eq system-type 'windows-nt) checks whether the system is MS Windows. Next, she searches the internet for how to check for MS Windows version in elisp. It turns out that she has to extract some internal version number from the variable system-configuration (which is a string) using some regular expression, and then if the extracted internal version number is something like 6.2, then the system is Windows 8. She is like “writing that logic is going to take more time than I expected”. But she is not worrying. She writes the big picture of the logic, like so:

(defun my-preview-config ()
  (when (<= 900 (x-display-pixel-height))
    (setq preview-scale-function 1.5)
    (when (eq system-type 'windows-nt)
      (let ((winversion
             --EXTRACT-WINVERSION--))
        (when (--BIGGER-THAN-OR-EQUEL-TO-6.2-- winversion)
          (setq preview-scale-function 'preview-scale-from-face))))))

The new part in above code should be read as: “Extract the internal version number and stores it to the local variable winversion. Then compare the value of winversion to 6.2, and if winversion is bigger than or equal to 6.2, then do (setq preview-scale-function 'preview-scale-from-face)“. Notice that the placeholder --EXTRACT-WINVERSION-- is there because she hasn’t yet figured out how to extract the version number. And the placeholder --BIGGER-THAN-OR-EQUEL-TO-6.2-- is there because she does not know how to compare version numbers yet. Notice that she just wrote above code before figuring out how to extract and compare version numbers.

Next, she look through the elisp reference to find what function to call to compare version numbers. She learns that version numbers are normally represented as lists of integers (and not as floats), and that version-list-<= is the function she should use. So she replaces --BIGGER-THAN-OR-EQUEL-TO-6.2-- to get:

(defun my-preview-config ()
  (when (<= 900 (x-display-pixel-height))
    (setq preview-scale-function 1.5)
    (when (eq system-type 'windows-nt)
      (let ((winversion
             --EXTRACT-WINVERSION--))
        (when (version-list-<= '(6 2) winversion)
          (setq preview-scale-function 'preview-scale-from-face))))))

Notice that she finished code for version comparison before finishing code for version extraction. There is no reason for you to always write expressions in chronological order.

Next, she opens a new elisp file. She writes code for extracting the version number by trial and error and testing things out and she does all that within the new elisp file. Finally, she arrives at the following code for extracting the version number:

(when (string-match (rx "nt"
                        (group (+ digit))
                        "."
                        (group (+ digit))
                        "."
                        (+ digit)
                        eos)
                    system-configuration)
  (list
   (string-to-number (match-string 1 system-configuration))
   (string-to-number (match-string 2 system-configuration))))

Next, she copies above code and paste it into where the placeholder --EXTRACT-WINVERSION-- is. The final result:

(defun my-preview-config ()
  (when (<= 900 (x-display-pixel-height))
    (setq preview-scale-function 1.5)
    (when (eq system-type 'windows-nt)
      (let ((winversion
             (when (string-match (rx "nt"
                                     (group (+ digit))
                                     "."
                                     (group (+ digit))
                                     "."
                                     (+ digit)
                                     eos)
                                 system-configuration)
               (list
                (string-to-number (match-string 1 system-configuration))
                (string-to-number (match-string 2 system-configuration))))))
        (when (version-list-<= '(6 2) winversion)
          (setq preview-scale-function 'preview-scale-from-face))))))

The way she wrote this function definition is mainly in the order of “from top level to bottom level”, but when she was figuring out the part for version extraction, she was probably building the expression in the order of “from bottom level to top level”. That is how Eve writes Lisp code: sometimes, top-down, other times, bottom-up.

Exercise 80. Repeat Eve’s workflow. Recall that Eve’s workflow involves basic operations only.

13. Preemptively breaking a line

Let’s say you have written this expression:

(+ a b (and (moo) (oink)) c)

Now suppose that you want to replace the moo form with a much more complicated moo form. Maybe you want to pass (+ 100 200) and (+ 300 400) as arguments to the moo form. But before doing that, you might want to break the one line expression into multiple lines first, so that you get:

(+ a
   b
   (and (moo)
        (oink))
   c)

Then you pass the arguments to the moo form, so that you get:

(+ a
   b
   (and (moo (+ 100 200)
             (+ 300 400))
        (oink))
   c)

The lesson here is that sometimes, breaking a one-line expression into multiple lines before you write more into the expression can be easier than breaking the line afterward,

14. Wrapping

Suppose you are writing a function that prints the sum of the three arguments and then prints a list containing the sum twice. So you write the following code and just after you write (print (list )), you suddenly realize, “wait, I should have stored the sum in a local variable”:

(defun my-func (foo bar baz)
  (print (+ foo
            bar
            moo))
  (print (list )))

So you cut the expression (+ foo bar moo) into clipboard, and write num in place, and you get

(defun my-func (foo bar baz)
  (print num)
  (print (list )))

and then you finish writing the second print form, and you get:

(defun my-func (foo bar baz)
  (print num)
  (print (list num num)))

and then you wrap the two print forms with a let form, and you get:

(defun my-func (foo bar baz)
  (let ((num (+ foo
                bar
                moo)))
    (print num)
    (print (list num num))))

Notice that going from our first code to this final code requires only basic operations, and that you have to use at least two clipboards (one for storing (+ foo bar moo) and another for storing the two print forms).

This kind of wrapping can be done more easily (and without requiring use of two clipboards) if your text editor has a command for what I will call the simple wrap operation. The simple wrap operation is wrapping one or more expressions with two parens and then indenting correctly. For example, when you have this code:

(defun my-func (foo bar baz)
  (print num)
  (print (list num num)))

and then you edit further to get:

(defun my-func (foo bar baz)
  let ((num --))
  (print num)
  (print (list num num)))

and then when you perform the simple wrap operation to the four expressions let ((num --)) ... (print (list ..)), you get:

(defun my-func (foo bar baz)
  (let ((num --))
    (print num)
    (print (list num num))))

and then you can paste back (+ foo bar moo) to get:

(defun my-func (foo bar baz)
  (let ((num (+ foo
                bar
                moo)))
    (print num)
    (print (list num num))))

Alternatively, you could have pasted it back before doing the simple wrap. A matter of preference.

Emacs Note: In vanilla Emacs, the simple wrap operation corresponds to selecting one or more expressions and then pressing M-( C-M-\ and that is because M-( runs the command insert-parentheses which is dwim.

If your text editor has a command for what I will call “forward slurp” then you can use that instead too. To demonstrate what it is, suppose again you have this code:

(defun my-func (foo bar baz)
  (print num)
  (print (list num num)))

and then you edit further to get:

(defun my-func (foo bar baz)
  (let ((num --)))
  (print num)
  (print (list num num)))

and then you call “forward slurp” command to cause the let form to slurp the print forms in, like this:

(defun my-func (foo bar baz)
  (let ((num --))
    (print num)
    (print (list num num))))

Emacs Note: Vanilla Emacs does not provide such a command. Paredit mode provides one. Paredit users should try M-x apropos-command RET paredit slurp to find it.

In your text editor, find a command or a plugin that provides “simple wrap” or “forward slurp”. We add these operations to our list of basic operations.

If you can’t find such commands, use the code templates feature to make them.

In case you are curious, the reverse of “forward slurp” is “forward barf” (according to paredit).

15. Unwrapping

To change this code:

(defun my-func (foo bar baz)
  (let ((num --))
    (print num)
    (print (list num num))))

into this:

(defun my-func (foo bar baz)
  (print num)
  (print (list num num)))

you can follow these steps: select the two print forms, then cut, then select the let form, then paste, then reindent.

Emacs note: I don’t know if vanilla Emacs provides commands for unwrapping, but paredit mode again provides one: the paredit-splice-sexp-killing-backward command which is bound to <M-up>.

Emacs note: To make pasting over selection work as expected, enable delete-selection-mode, if you don’t want to enable cua-mode for some reason.

16. Inserting in the middle

Suppose we have this code.

(moo (oink 2
           (cow x))
     (oink 2
           (cow x)))

Suppose you decide you want to insert another argument to the moo form, between the two oink forms. In particular, let’s say you want to change above code to this code:

(moo (oink 2
           (cow x))
     (let ((x 1) (y 1))
       (+ x y))
     (oink 2
           (cow x)))

Here is a way of doing that. First, change the original code to the following code (by opening a blank line in the right place and then inserting () on that blank line):

(moo (oink 2
           (cow x))
     ()
     (oink 2
           (cow x)))

and then, to this:

(moo (oink 2
           (cow x))
     (let ())
     (oink 2
           (cow x)))

and so on.

That is how you insert an argument as a middle argument (as opposed to the first or last argument) of a form.

Exercise 90. Insert the expression (+ 50000 60000) into the right place in the following code:

(setq blah (list (+ 10000
                    20000)
                 (+ 30000
                    40000)
                 (+ 70000
                    80000)))

17. Appending

Suppose we have this:

(defun my-blah (x)
  (foo (la (la 1 x))
       (bar (moo 123
                 (oink 2
                       (cow x)))))
  (la 1))

Notice that the moo form is given two arguments: 123 and the oink form.

Now suppose you want to insert (let) as the last argument to the moo form. How would you do that?

One way of doing it is to first break the second-to-last line at the right place to get:

(defun my-blah (x)
  (foo (la (la 1 x))
       (bar (moo 123
                 (oink 2
                       (cow x))
                 )))
  (la 1))

and to do that, you either

  • use the “show me which open paren matches this close paren” feature of your text editor to place the text cursor at the right place before breaking the line, or
  • place the text cursor at the start of the oink form, and then invoke the command for “move past one expression” to move past the oink form, then break the line.

After breaking the line, it should be easy to insert (let) to get:

(defun my-blah (x)
  (foo (la (la 1 x))
       (bar (moo 123
                 (oink 2
                       (cow x))
                 (let))))
  (la 1))

Another way is to first get:

(defun my-blah (x)
  (foo (la (la 1 x))
       (bar (moo 123
                 (oink 2
                       (cow x)))))
  (let)
  (la 1))

and then repeat “forward slurp” at right places until (let) ends up at the right place.

Emacs note: If you use paredit, you just move point to any whitespace before (cow x) and then invoke paredit-forward-slurp-sexp repeatedly until (let) is at the right place.

Emacs note: Yet another way is to use M-) which is bound to move-past-close-and-reindent in vanilla Emacs. If the point is somewhere inside (cow x), which would be the case if the cow form was the last thing you wrote, then you can just press M-) multiple times until point is at the right level, and then you write (let).

After that, you can fill details of the let form if you want. So suppose you have done that and you now have this code:

(defun my-blah (x)
  (foo (la (la 1 x))
       (bar (moo 123
                 (oink 2
                       (cow x))
                 (let ((z 1))
                   (+ x z)))))
  (la 1))

Now suppose you want to delete the let form now for some reason. If you delete it, then you get something like:

(defun my-blah (x)
  (foo (la (la 1 x))
       (bar (moo 123
                 (oink 2
                       (cow x))
                 )))
  (la 1))

and then you can join lines to get back to the original code, if you want.

Now you know how to append something to a form, and how to do the reverse.

Exercise 100. Suppose you have this code:

(defun my-search-foo ()
  (catch 'loop
    (let ((i 0))
      (while (< i 10)
        (let ((j 0))
          (while (< j 10)
            (if (foo i j)
                (throw 'loop (list i j)))))))))

Insert two cl-incf forms at right places to get this code:

(defun my-search-foo ()
  (catch 'loop
    (let ((i 0))
      (while (< i 10)
        (let ((j 0))
          (while (< j 10)
            (if (foo i j)
                (throw 'loop (list i j)))
            (cl-incf j)))
        (cl-incf i)))))

(Above is taken from this gif)

18. Prepending

There is a useful operation that I call “move rectangle down”. I will show you what it is, using JavaScript example.

Suppose you have the following JavaScript code and that the text cursor is between var and height:

var height, // blah
    width,  // blah lorem
    size;   // blah lorem
console.log("hello");

When you perform the “move rectangle down” operation, you get the following code and it leaves the text cursor at the end of var:

var
    height, // blah
    width,  // blah lorem
    size;   // blah lorem
console.log("hello");

Because the text cursor is at the end of var, you can then just start typing something so that you get, for example:

var speed,  // blah
    height, // blah
    width,  // blah lorem
    size;   // blah lorem
console.log("hello");

Find the command for “move rectangle down” in your text editor. If you can’t find it, it is easy to make that command, because the operation is simply “type newline and then type spaces as many as the length of the previous line and then move to the end of the previous line.”

Emacs note: It’s the command split-line which is normally bound to C-M-o.

This operation is different from what we discussed in the “breaking lines” section, but nevertheless the reverse of this operation is the thing we discussed in the “joining lines” section.

Exercise 110. With help of the “move rectangle down” operation, change this JavaScript code3:

var myString =
   ["Monday.",
    "Bus day.",
    "Happy birthday."
   ].join('\n');

to this code4:

var myString =
   ["What day.",
    "Monday.",
    "Bus day.",
    "Happy birthday."
   ].join('\n');

You can also use this operation to prepend something to a form, that is, insert something as the first argument to a form.

Exercise 120. Suppose you have this code:

(setq blah (list (+ 30000
                    40000)
                 (+ 50000
                    60000)))

Insert (+ 10000 20000) and (+ 70000 80000) at right places to get this code:

(setq blah (list (+ 10000
                    20000)
                 (+ 30000
                    40000)
                 (+ 50000
                    60000)
                 (+ 70000
                    80000)))

Exercise 130. Now do the reverse.

Actually, there is another way of prepending something to a form. When you have this:

(setq blah (list (+ 30000
                    40000)
                 (+ 50000
                    60000)))

You could type () at the start of the first argument, to get:

(setq blah (list ()(+ 30000
                    40000)
                 (+ 50000
                    60000)))

and then break the line to get:

(setq blah (list ()
                 (+ 30000
                    40000)
                 (+ 50000
                    60000)))

and then fill details of the new first argument.

Exercise 140. (Fizz buzz) Write a program (in Common Lisp or Emacs Lisp) that prints the numbers from 0 to 99. But for multiples of 3, print “Fizz” instead of the number and for the multiples of 5, print “Buzz”. For numbers which are multiples of both 3 and 5, print “FizzBuzz”. Write that program by inserting appropriate clauses to the cond form in the following unfinished code:

(dotimes (i 100)
  (cond ((zerop (mod i 3))
         (print "Fizz"))
        ((zerop (mod i 5))
         (print "Buzz"))))

Exercise 150: Change this code

(setq blah (list (+ 10000
                    20000)
                 (+ 30000
                    40000)
                 (+ 50000
                    60000)
                 (+ 70000
                    80000)))

to this code:

(setq blah (list
            (+ 10000
               20000)
            (+ 30000
               40000)
            (+ 50000
               60000)
            (+ 70000
               80000)
            ))

Exercise 160: Now do the reverse.

(Hint: the previous two exercises just reduce to breaking or joining some lines and then reindenting. For reindenting, you might want to select the list form and then invoke the command for reindenting the selected expression, rather than fixing indent line by line.)

Emacs note: this is where C-M-u comes in handy. In vanilla Emacs, C-M-h runs the command mark-defun which selects the top level form. Also, if you use paredit, you can use the command paredit-reindent-defun which is bound to M-q to reindent the top level form.

19. Automate

Welcome to the final phase: the automation phase. In this phase, you just automate things as much as you feel necessary. Here are some ideas you can take if you like:

  • Configure your editor so that correct indentation is done every time you paste something.
  • Configure so that a close paren is automatically inserted every time you insert an open paren.
  • Make each basic operation into a command.
  • Make some often used sequence of basic operations into a command.
  • Code templates

Emacs note: paredit takes care of some of above ideas.

20. Further reading

Google Common Lisp Style Guide gives you some idea on how you would write and format Lisp code, some of which you might apply to other dialects of Lisp too. There is probably no need to follow everything in that guide.

20.1. Further reading for Emacs users

This post is part of the Living with Emacs Lisp series, and the next thing you should read in this series is probably something about “how to debug init code”, which I will write soon.

If you are interested in writing Common Lisp within Emacs, start with my SLIME starting guide.

For Emacs Lisp, http://sachachua.com/blog/series/read-lisp-tweak-emacs/#post-27273

21. Dancing chickens

Domino’s Techno Chicken:
http://www.youtube.com/watch?v=vHoi8OqWUF0

Admit it, you want to watch that again even though you have already watched it years ago.

Footnotes:

1

which is similar to what Terence Tao called the three phases of math education, which in turn is similar to Douglas Adams three phases of civilization

2

There are two places between them. You’ll see if you use the bar-shaped text cursor instead of the box-shaped one.

4

This list of strings is actually a conversation that can happen between an English person and a Korean person, each of them knowing only one language.

This entry was posted in Emacs, Lisp and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s