HACKER Q&A
📣 HexDecOctBin

Is structural editing considered mandatory for Lisp?


I recently tried using Lisp (CL and Racket) again after college, and ran into a problem that doesn't really seems to find any mention in books and evangelising blogs.

All style guides seem to agree that in Lisp, all closing parenthesis should be put in the last line of code (instead of stacking theme line-by-line C style). However, if you do this, and then want to add more code after the last line, you either end up having to move cursor to correct location by counting parenthesis (usually augmented with editors' matching paren highlighting functionality); or just delete all the closing parens, add your code and add close all of 'em again. Both are tedious ways of writing code.

Looking around the internet, the "solution" I find is to edit the Lisp code structurally through editing in whole s-expressions. This seems yo imply that if I want to write Lisp without pulling out my hairs, I need to first learn a new way to edit code and only then go to actual programming; not to mention that always being stuck in the constraints of sexprs would inevitably be too..., well, constraining.

Is this true? Is learning Lisp should really be preceded with learning structured editing? And if so, how come no learning material or style-guide ever mention this practical hurdle?


  👤 frou_dh Accepted Answer ✓
You're overblowing it. Yes paredit etc will be faster, but just using normal editing in an editor that has decent generic treatment of parenthesis and autoindent is not too bad, and certainly not unusable.

👤 kazinator
In Vim, I put the cursor on the start of the previous expression. Then hit % to jump to the matching closing parenthesis. Then type a to add material. The next keystroke after that will be Enter. If I expect to be adding more material in the same session, I may leave the stack of closing parentheses on their own line temporarily. Then cuddle them up to the previous line when done.

👤 dreamcompiler
I write Lisp very quickly and I don't use paredit or parinfer. I depend on Emacs' automatic paren matching, auto-indent, indent-sexp, and forward/backward sexp functionality but that's it. I guess I'm just used to it.

I don't ever find myself counting parens.


👤 samsaga2
I don't know what editor you are using, but I think you really need a parenthesis plugin. Something like paredit for emacs. A single parenthesis in a line is a waste of space and makes the code harder to read. Once you know how to move it cross lisp, the parenthesis is a blessing. You can remove or move whole blocks of code in a fraction of a second.

👤 cpach
As with many things in programming, it comes down to personal preferences.

Some people love Perl, some people hate it. Some people love C++, some people hate it. Some people love VS Code, some hate it. Etc etc.

Same thing with Lisp and the parentheses.

No language has perfect syntax, so I would say go with the language that irritates you the least.


👤 lispm
Let's say we have a function

  (defun foo ()
    (bar 1
        (baz)))
Helpful for the developers are:

* automatic parenthesis matching with highlighting matching pairs.

* colored parenthesis pairs

* syntax highlighting

* automatic indentation

* structural editing commands, up to rare editors which are structural only

If I would expect that I would often extend the function above by adding another function call, then I might write it as:

  (defun foo ()
    (bar 1
        (baz))
    )
and then add a call

  (defun foo ()
    (bar 1
        (baz))
    (foo)
    )
But that's rare and sometimes used in configuration files.

Let's look at this:

  (defun foo ()
    (bar 1
        (baz)))
Now we to add a new argument to the call to bar. What I would typically do is this:

* put the cursor in front of one of the arguments. Here just (baz). The I would move over the s-expression to the end of the s-expression. A Lisp editor mode will have simple commands to move over s-expressions.

* the I would insert a newline and indent. A good Lisp editor will have a single command for that. The result is:

  (defun foo ()
    (bar 1
        (baz)
        ))
The cursor will be in front of the )). Now we can enter the Lisp next form.

  (defun foo ()
    (bar 1
        (baz)
        (hello)))
Now one can also think of this in terms of list manipulation: add a new form to the end of a form.

The goal when you edit Lisp forms are:

* use the list structure as help to navigate in the form

* always keep the list structure properly indented and never do that manually, the editor has commands for that

* always keep the code compact

* don't format the code to make parentheses stand out. With more experience a Lisp developer will not use parentheses themselves a useful visual element. Learn to see whole expressions and not the isolated parenthesis as a visual token. It's a bit like learning a bike. Beginners think that they need to concentrate on keeping the bike stable. Later that's automatic.

* format the code such that indentation makes blocks visible (think of Python without parentheses for a similar example)

* learn the editor commands to move and edit list expressions

Lisp editors usually have a bunch of commands to work with s-expressions. Additionally there are specialized additions like 'paredit' and 'parinfer'. The latter infers parentheses from indentation.

Summary:

You don't need it when starting to learn to edit Lisp code, but over time it is useful to learn more about editing list expressions. Lisp code is made of list forms. Learn to make use of that as an advantage. The mental model of seeing code as simple list operations in the editor is actually relatively obvious and simple to learn.

Learn Lisp piece by piece. Incremental learning is aided by the tools.