Search code examples
lispcommon-lispreadabilitycode-readability

How to effectively read and write Lisp code?


I'm currently learning Lisp, and I think that I got the basics (I'm using the excellent book Land of Lisp, and so far I've read and worked through about a quarter).

I try to create my own Lisp programs, based on what I already learned. Somehow, it works. But it's only somehow. So far I have been developing mainly in languages with C syntax, such as C# and JavaScript (and please note that I'm perfectly aware that JavaScript is not a C-based language).

Nevertheless, I'm used to "think" in C syntax, and when I write code in C# or JavaScript, I can write it down in a quite straightforward way. In contrast, when writing Lisp code I have enormous difficulties wrapping my mind around all those parentheses.

If I have a simple statement such as

(setf x (+ 2 3))

I always find myself in trying to read it from left to right, find out that it does not work, then search for the innermost pair of parentheses, and then work it out inside-out. For this simple expression, this works quite fast.

But if I have more complex code (although it is not yet complex at all), say a function that uses let, it's harder (at least for me) to find the innermost pair of parentheses:

(defun foo ()
  (let ((x 23)
        (y 42))
  (+ x y)))

Here it's already a little bit harder to see what happens after what, and what is nested to what. Now add some cond stuff, perhaps combined with a few lambdas, and I'm perfectly lost and find myself counting parentheses for minutes (literally).

The same is true when writing it, I get lost in the number of parentheses, and I don't think that I even saw "complex" Lisp code yet.

Does this get better over time? I.e., do you get used to it? Or, are there tricks on how to approach reading and writing Lisp code to make things more easy for yourself? How do more experienced Lisp programmers do this? Any hints?


Solution

  • You need to indent it properly. Your example is not.

    (defun foo ()
      (let ((x 23)
            (y 42))
      (+ x y)))
    

    Here:

    (defun foo ()
      (let ((x 23)
            (y 42))
        (+ x y)))
    

    The last expression needs to be shifted to the right. Then you can see that it is inside the LET.

    Lisp has a few structure patterns. Not too many. Once you learn and training those, reading Lisp is relatively easy. Much less hard than learning Chinese which is read by a lot more people.

    some patterns:

    (symbol ...)
    

    the symbol gives the anchor

    ((a 10) (b 20))
    

    binding list, for example in LET

    (with-foo (foo :option1 1 :option2 2)
      (body))
    

    Macros like WITH-OPEN-FILE.

    Then the complex function arglist itself. Positional args, optional args, keyword args. &rest and &aux.

    There are a few more, but not too many. Once you have learned the basic macro and special form patterns, code reading gets much easier.

    How to improve that? Read code. Train. It's not really difficult. A little bit like bicycle riding. First days seem to be difficult, then it's automated.

    Writing.

    To write code, you need an editor which can indent. Most can only indent lines, but can't format whole expressions. Thus you need to do the formatting. There are a few basic rules. Lisp has a pretty printer, which can format. But that's often not usable inside the editor and it does not know about things like comments.

    When I write a piece of Lisp code, I usually spend also a bit of time for the proper layout of the code.

    • Lines too long?
    • Dangling parentheses?
    • Correct structure?
    • Alignments?
    • Enough visual clues?
    • Naming?
    • Comments?
    • Documentation?