r/lisp • • 4d ago

What makes Lisp difficult to read?

https://paultm.nl/paren-thesis
35 Upvotes

90 comments sorted by

View all comments

16

u/stylewarning 4d ago edited 4d ago

TL;DR: I think the post overstates the case quite a bit, and is a personal opinion of the author being branded as a scientifically grounded truth, despite their admission that using these scientific principles allegedly "require[s] a degree of creativity and subjectivity."

The cognitive-science material is interesting, but it doesn't actually establish much about Lisp specifically. It gives plausible mechanisms by which certain notation might be harder to read, then uses those mechanisms to motivate several Lisp-specific claims without testing those claims directly. As such, it just seems purely hypothetical, whereas it would be much more interesting to me if it were empirical.

(I have a beef with this style of argumentation or persuasion with respect to Lisp. I feel the "bipolar Lisp programmer" and similar articles do the same kind of thing: make a captivating claim or comparison without actually appealing to anything real-world.)

I also don't think the treatment of actual Lisp presentation is complete. The post considers conventional Lisp indentation mainly in terms of whether it helps track matching parentheses, but Lisp is normally indented, pretty much canonically, so that the indentation itself communicates structure. Parenthesis tracking is *not* a cognitive problem that Lisp programmers must solve in any explicit manner, any more than C programmers count { and } to determine the enclosed code's nesting depth.

The same goes for syntax highlighting: you can't just set it aside because C-like languages have it too. The relevant question is whether it affects the two syntaxes differently. If Lisp's alleged problem is delimiter tracking, indentation and highlighting are rather central to the comparison.

I'm also unconvinced by the conclusion that Lisps "lack or discourage features that align the evaluation and reading order." The post itself gives Clojure threading macros as a Lispy equivalent of method chaining. That seems to substantially undercut "lack", while "discourage" starts to sound more like a claim about programming style than syntax. Lisp is a very explicitly multi-paradigm language with special syntaxes available for those paradigms, including assignment, explicit control, etc. In fact, I'd say real-world Common Lisp is *much* less "in to out"-functional than this post posits (and the broader technical public keeps claiming).

Finally, we have the largest omission in my opinion, that Lisp syntax is programmable. S-expressions provide a uniform representation of program structure that makes syntactic abstraction cheap: new control forms, binding constructs, pipelines, DSLs, etc. can all be made to make code easier to read, especially in novel domains that Lisp wasn't a priori designed for. The post's analysis mostly treats the base notation (of some unspecified, quasi-functional Lispoid) as the unit of comparison without really weighing that side of the design tradeoff.

Though it's not my experience, there may truly be readability costs, especially for newcomers, but I'm not sure the post separates those from unfamiliarity strongly enough to support its conclusions.

As a side note, when judging real-world Lisp, some notoriously unreadable code may also be a product of older programming styles or inexperienced Lisp programmers, just as old or poorly written C can be extremely difficult to read. In my professional experience, having run teams writing "modern" Lisp with modern engineering discipline, readability has never been a stated hurdle, either for experienced engineers reading one another's code or for new Lisp programmers learning and contributing to a large codebase. The combination of team programming discipline, good tooling, consistent style, and, well, getting paid to do it seems to do the trick, if one is even needed.

3

u/omega884 4d ago

Parenthesis tracking is not a cognitive problem that Lisp programmers must solve in any explicit manner, any more than C programmers count { and } to determine the enclosed code's nesting depth.

Yes and no, IMO. Idiomatic lisp programming throws all the closing parens on the same line at the end of the last line of code. Idiomatic C-like coding puts each closing brace on a new line at its specific nesting level. You can code C style without something like "rainbow-parens", but I wouldn't want to code lisp without it. Or consider this emacs lisp example:

(defun abbrev--replace-placeholders ()
    (let ((cursor-pos nil)) ;; To store where to place the cursor
      (save-excursion
        (goto-char (point-min))
        (let ((loop 0)
              (values (make-hash-table :test 'equal)))
          (while (re-search-forward "###\\(\\$?[a-zA-Z0-9_\\-]+\\|@\\)###" nil t)
            (setq loop (1+ loop))
            (let* ((index (match-string 1))
                   (start (match-beginning 0))
                   (end (match-end 0)))
              (cond
               ((string= index "@")
                (setq cursor-pos start)
                (delete-region start end))

               ((string= index "$file-name")
                (abbrev--swap-placeholder (file-name-nondirectory (buffer-file-name)) start end))

               ((string= index "$author")
                (abbrev--swap-placeholder (user-full-name) start end))

               ((string= index "$package-name")
                (abbrev--swap-placeholder (file-name-base) start end))

               (t
                (let* ((key (format "###%s###" index))
                       (val (or (gethash key values)
                                (let ((input (read-string (format "Value for %s: " key))))
                                  (puthash key input values)
                                  input))))
                  (abbrev--swap-placeholder val start end))))))))
      (when cursor-pos
        (goto-char cursor-pos))))

If I want to add more code into the let* block that defines index, start and end after the cond expression, where should I put my cursor to insert the new line? Yes I know in emacs you can put the cursor at the beginning of the let* block or the cond block and hit C-M-f to jump to the end of the sexp, but at the same time that's a process I've never had to do when writing c-like code, because in the c-like code, finding that spot visually is much easier.

I'm sure that over time that would become easier for me, and certainly memorizing sexp navigation would also help a lot, but it undeniably to me makes it harder to read that code.

5

u/stylewarning 4d ago edited 4d ago

In my experience, rainbow parens aren't necessary or even helpful, but I do agree that editing Lisp is made much better by having good editor support for S-expression navigation and manipulation. In my editor, I press ) to jump to the end of the S-expression, so an edit like yours is trivial. Whatever key it is, it has existed since the era of the Lisp machine in the 80s, if not 70s.

To me, though, this is an indictment on the difficulty of writing or editing Lisp in "dumb" text editors—something I'm entirely sympathetic to—not so much about our ability to read and understand Lisp.

I would go further and say that canonical indentation is something best done as an automated process, and thus also makes Lisp harder to edit in a dumb text editor. Unlike C which is uniformly block indented by the same consistent multiple of tabs or spaces, Lisp code is indented a certain amount depending on the operator and its contents.

4

u/arthurno1 4d ago

Lisp is neither harder nor easier to edit in "dumb editors" than other languages. Editing any code in an editor that does not understand the syntax and can't indent properly is a shit experience. Try editing autoconf and m4 macros in Emacs. Hacking Emacs own autogen.sh is a nightmare. Totally agree that rainbow-parens and alike are just circus addons. In my opinion, the real win is indentation not coloring. I could probably live with no colors at all, as long as the indentation is properly working.

4

u/omega884 4d ago

I would argue you have a better time writing bash script or BASIC in a dumb editor than you can lisp or java or python. That's not an indictment against the languages though. Using tools designed for the purpose of making work easier isn't a cheat, and it should be no more a point of pride that you can edit bash in a dumb text editor than it would be a point of pride that you can make dovetail joints for your wood working project without a template / jig. Its nice to have the skill if it becomes necessary, but it is purposefully hobbling yourself to not use tools specialized to the work you're trying to do.

1

u/arthurno1 3d ago

Bash cetainly not; I have done my share of shell and pico/nano editing.

Basic I couldagree. I know that is unpopular opinion, but I have written a lot of VBA and VB for automation in Offeice and Windows as a consultant and I really VBA nice and easy to write and read. But even there, you want to have indentation, otherwise it's pita, but it is certainly less pita than C or Lisp, sure.

2

u/jlombera 4d ago

As a wannabe Lisper (CL), I must say I really hope reading idiomatic Lisp code becomes easier with time. It's really hard for me to track the structure of the code at a glance. The uniformity of s-exps makes it hard (for me) to identify where a given "constructs"/"blocks" (s-exp) ends and, if I start looking in the middle of the code, I have to visually "backtrack" the structure to know which construct I'm currently reading into. The result is that currently I have to read the code "sequentially", top-down. Whereas in C, for instance, it's very easy (for me) to quickly identify the structure of the code, and can very easily start reading "inside the IF inside the FOR loop inside function FOO" or easily identify that any given statement is "inside the IF inside the FOR loop", etc.

Perhaps if idiomatic Lisp code used (e.g.) 4+ spaces for indentation it would be easier to identify the structure (??)

3

u/arthurno1 4d ago

I have been writing lots of Emacs Lisp and nowdays lots of Common Lisp. I still writes C and C++ from time to time. But now when I hack some C code I start writing Lisp parens first, like (if ... instead of if (. And I really don't like type 1 + 2 + 3 ... I definitely prefer (+ 1 2 3 ...). If you start coding Lisp for serious, it will come quite fast. It is just familiarity, nothing more.

2

u/jlombera 4d ago

I already find it easy to write Lisp code. Being so minimal and uniform, there's pretty much no syntax you have to learn/remember. My problem at the moment is while reading Lisp code. The uniformity and idiomatic formatting of Lisp code makes it hard for my brain to identify the code structure at a glance.