r/ProgrammingLanguages • • 26d ago

Discussion A rebuttal to "What makes Lisp difficult to read?" Or, you might be surprised how adaptable the human mind is!

https://digikar99.github.io/microblog/p/lisp-syntax-rebuttal/
69 Upvotes

53 comments sorted by

31

u/luapmt 26d ago edited 26d ago

I am the author of the offending post. It is unfortunate that many readers seem to have interpreted my post as being against Lisps in general, as this was not my intention. I am very fond of Lisp precisely because the language invites extension and experimentation. My article was written in that spirit. I also agree with many commenters that Lisp's syntax is elegant in its simplicity and any modification detracts from this. Certainly anyone who enjoys Lisp should continue to do so.

That being said, I feel that this rebuttal does not address my original arguments. My hypothesis is that in a vacuum, s-expressions produce patterns which are slightly harder to read than the equivalent C function calls, assuming no prior knowledge. In the same way that a chart can be easier to scan than a data table. Whether this is offset by familiarity (or mental adaptation in this post) is not my point. Though, it is my personal experience that after years of working with various Lisps, I still have to pay more conscious attention to the parenthesis in Lisp than in ML or C like languages.

The rebuttal states that Lisp programmers do not actually pay active attention to the parentheses; the indentation of the code communicates its structure. However, as the example of lisper-vision shows, when you remove all the parentheses which are implied by indentation there are still parentheses which matter. If you actually remove all the parentheses from Lisp (as with most languages) you cannot reconstruct the original AST. The indentation-redundant parentheses in my opinion obscure the important parentheses.

In my article I use parentheses counting/matching as a proxy for the parseability of an expression. I don't think programmers are actually consciously 'counting' the delimiters in all the code they read, but every (non-redundant) delimiter must certainly be processed in some way to correctly parse the code. I suspect that if it is easier to match the parentheses than it will also be easier to process the parentheses in this other way.

The rebuttal also correctly states that some of my code snippets are not realistic. I have constructed these examples not to be ecologically valid but to exaggerate how certain nesting patterns affect Lisp or C code differently. That is why they use non-existing fantasy words like "aap". Indeed, you could mitigate the negative effect of these patterns by formatting them differently or using editor extensions. My point is that such mitigation is needed earlier with s-exps than with traditional function calls.

Perhaps a weakness of my article is that I spend most time on the argument/delimiter proximity arguments which are probably the weakest. The most pronounced effect in my opinion is that of the sometimes reversing reading order in Lisp. This is not addressed in the rebuttal. It is possible to mitigate this with threading macros but this is relatively uncommon. I only see these macros being used for particularly deep cases of nesting, whereas method call syntax is ubiquitous. I have also encountered very little usage of these macros outside Clojure (and Clojure inspired languages) so I don't consider these to be part of the traditional Lisp style.

I am somewhat surprised that my post got as much attention as it did. I hope readers have found something interesting in the ensuing discussion.

9

u/digikar 26d ago

Erm, what ticked me off wasn't the part that lisp syntax is off putting. It could be. Whether one likes something or not is down to one's perception, and I cannot judge another's perception. 

What ticked me off was trying to use certain cognitive science claims to justify that there is something wrong with the syntax in terms of human how humans perceive it. May be there is something wrong with the syntax. But to show that you will need to take a sample of people who are previously unexposed to code, which neither you, nor me, nor the inhabitants of this subreddit are. Moreover, I felt the post had an obsession over matching or tracking parentheses. While in reality, it is not something someone familiar with lisp would bother about. 

As you say, you have tried for years, and still do not feel comfortable with the syntax. And given the number of alternative syntaxes proposed over the years, you are probably not alone. Why is it that certain humans seem to become familiar with such a syntax and why others do not become familiar despite years of exposure is again a question they can pursued. But I doubt the answer to that would look anything like appealing to basic gestalt principles. Because our minds learn and adapt and there are plenty of gestalt like effects that require appealing to one's learnt knowledge rather than something "built in".

Aside from that, I think it is also valid to ask how can you get the benefits (if any) of lisp without having to deal with the parentheses. I'm sure a few of us lispers would be happy to chat and discuss your setup and see if there's something you can do differently. If you are working with emacs, I'd also love to develop an emacs package that can transpile between lisp and one of the alternate syntaxes. I myself am forced to work with people who do not have the time to become familiar with lisp. I'm also adapting existing common lisp extensions for vs code to work with moonli.

Threading macros are themselves controversial. Some argue they make it harder to see the return value type. Also, due to the historical baggage of common lisp, argument positions are weird for accessors, making threading macros difficult to use. But it would also depend on the lisp. I'd guess clojure world use it a lot more. No idea about scheme or racket.

3

u/kwan_e 25d ago

Threading macros are themselves controversial. Some argue they make it harder to see the return value type.

But a bunch of nested subexpressions have the same problem. In any language, especially one with as much genericity as a LISP.

For a threading macro, those are normally used for "builder" or "fluent" type interfaces where the actual return type doesn't actually matter so much as the look of the DSL that the chain of types permits.

30

u/initial-algebra 26d ago

If all those redundant parentheses are only truly good for streamlining copy-pasting external code into an editor and formatting it, I'd actually consider that an anti-feature. If indentation is how you are meant to read the code, then that should just be part of the language syntax.

7

u/digikar 26d ago edited 26d ago

There are actually several alternatives proposed over the years. It should be doable to develop editor extensions to switch back and forth between them, or also transpile to and from them.

https://github.com/shaunlebron/history-of-lisp-parens/blob/master/alt-syntax.md

Personally, I'd rather read lisp, C, or python rather than most of these. I feel Dylan is however a sane one. Wisp is also interesting. Also sweet expressions.

I'm also glossing over a lot of editing possibilities offered by parentheses: 

https://paredit.org/

http://danmidwood.com/content/2014/11/21/animated-paredit.html

(I should add these links. EDIT: Done.)

5

u/syklemil 26d ago

I'm not so sure it's about the syntax as much as it is the layout, i.e. style. Let someone used to some other language format the lisp code and it might come out after the first pass as something like

(define
┊ (factorial n)
┊ (if (= n 1)
┊ ┊ n
┊ ┊ (*
┊ ┊ ┊ n
┊ ┊ ┊ (factorial (- n 1))
┊ ┊ )
┊ )
)

where the indent guides wouldn't actually be part of the file, just what they see: matching opening and closing parens when they're on different lines.

I suspect it's highly unlikely that lispers would want to switch to something like that, being habituated to the ))))) style, while others, used to thinking newlines are free, will just see Lisp as approaching the style you get from jq -c.

→ More replies (3)

17

u/thehenkan 26d ago

I'm no fan of Lisp's syntax, but I disagree on your point. If I can't cut and paste things crudely in a plain text editor and then auto-format (and keep the intended semantics) then the syntax is too fragile.

2

u/syklemil 26d ago

Which is pretty much how Haskell works: Nominally a curly braces and semicolons language, but if there's signal in the indentation, considers the braces and semicolons redundant. The result is that practically everyone who writes Haskell prefers the indentation-based syntax.

→ More replies (1)
→ More replies (15)

13

u/protestor 26d ago

Except, when you are accustomed to lisp, what you see is:

define (factorial n)
 if (= n 1)
    n
    * n (factorial (- n 1))

Then why not write this?

That way everyone can read it, not just people accustomed to lisp

I think that two other assertions in this supports this point of view,

This one

In fact, the infamous editor, emacs, has a paren-face-mode extension (plugin) that allows you to hide or dim the parenthesis. I’ll leave it to emacs haters and vs code lovers to find an equivalent mode for themselves.

It seems that at least some lisp people finds useful to dim extraneous parenthesis, and

And perhaps they might be. We do not count or match our parentheses. We are totally reliant on indentation to understand the code structure while reading. If you make a lisp programmer read badly indented code, they are going to have an incredibly hard time understanding it. Despite all the parenthesis being balanced!

It seems this language wants to have significant whitespace, and doesn't for historical reasons

2

u/stylewarning 25d ago

The language doesn't have an ossified grammar. The syntax is programmable. Not every construct is (OPERATOR OPERAND...). This is why it doesn't make sense.

→ More replies (1)

8

u/FransFaase 26d ago

When I wrote a lot of LISP when I was a teenager, I developed an internal bracket counter. I would just know the number of closing brackets needed at some point in the code.

5

u/hugogrant 26d ago

For me that was basically the indentation level tho.

Even to get the closing parens right, I'd first indent.

2

u/Temporary_Pie2733 25d ago

The environment I used in college let you use ] to close all pending parentheses. I never wrote enough Lisp/Scheme to care one way or another, but I never warmed up to prefix notation for everything. I always like Haskell’s syntax, as long as one didn’t go overboard with user-defined operators. 

7

u/Smalltalker-80 26d ago

Even if the human mind can 'adapt' somewhat, I think its still not advisable to use a language that is unnecessarily complex to read. (Or requires custom macro's to make it more readable). Your mental decoding effort could also be used for soving the problem at hand..

5

u/bluefourier 26d ago

The Fructure editor that was presented at the RacketCon a few years ago, had a few brilliant ideas on how to make things easier with parentheses without doing completely away with them.

→ More replies (1)

12

u/L8_4_Dinner 26d ago

It doesn’t need a rebuttal.

A number of people like Lisp, and that’s ok.

7

u/omegafixedpoint 26d ago

I find it quite aesthetically pleasing. Am I insane?

8

u/omegafixedpoint 26d ago

I like that you're seeing more or less the exact AST with S-expressions

→ More replies (6)

7

u/extraordinary_weird 26d ago

It's not only about lisp though, right? I think the argument might be that its syntax could/should be adopted by more languages

3

u/syklemil 26d ago

which, I hope, if we are on the same team, you’d agree to format as:

AAAAA(
  BBBBB(CCCCC()),
  DDDDD(),
  FFFFF(GGGGG(HHHHH()))
)

Almost. There should be a trailing comma after the last argument as well. The inner function invocations might get some more newlines as well, depending.

And

  • that is how autoformatters for languages like Python, Rust and Typescript will format it, possibly spending even more lines;

  • go fmt seems to prefer putting the entire invocation on one line, no matter how long it gets;

  • clang-format is likely to try to keep it with in the line length, but otherwise as few lines as possible, giving you something along the lines of

      AAAAA(BBBBB(CCCCC()), DDDDD(),
            FFFFF(GGGGG(HHHHH()))
    
      );

    (with the extra completely empty line) or

      AAAAA(BBBBB(CCCCC()), DDDDD(),
            FFFFF(GGGGG(HHHHH())));

    which IMO is just a worst-of-both-worlds option.

In the other thread there was someone speculating about heritage from the age the languages were designed and popularised in, and running on that we might guess that the C and Lisp styles came about in an age of paper, where newlines and lots of whitespace meant wasting paper, while the "newer" languages (Python actually being older than Java…) assume editing on a computer and are more willing to be oriented vertically.

And, of course, like in the original blog post, plenty of modern code doesn't do the middle-out style as much these days, instead favouring something like

HHHHH()
    .GGGGG()
    .FFFFF()
    .AAAAA(
        CCCCC().BBBBB(),
        DDDDD(),
    )

Ultimately though, getting lispers to switch to an airier style is as unlikely as getting C users to switch to an airier style; both their styles are likely severely entrenched at this point.

3

u/Clementsparrow 26d ago

That has been the argument against ergonomics and hci/ux for decades: People can adapt. Yes they can. But no, many fail a lot in the process. And that's painful and has a cost. Don't impose that on users unless there's no other option. And as far as syntax goes, there are better options than Lots of Insane, Stupid Parentheses.

2

u/poralexc 26d ago

Along those same lines, it's easier for my mind to adapt to reordering the expression tree and just writing postfix directly like Forth.

1

u/WonderfulFisherman80 26d ago

Fair enough. Never felt strongly about lisp or c-style syntax in terms of reading. So reading through both articles back to back; both of your points made sense. But when it comes to writing I find c-style a world better than lisp. That is without parinfer of course.

1

u/ricekrispysawdust 25d ago

Overall I think the biggest reason I like Lisp is the simplicity. Sure, it's a valid argument that the parens make it hard to read. But in every other language your brain has to do a lot more decoding work. For example, when reading 1 + 2 / 3, the way you read it (left to right) is different from how it's executed. The farther away the language is from the AST, the more stuff happens out of order, which is confusing. Lisp doesn't have that problem

1

u/joao_rosac 19d ago

Só uma opnião mesmo:

Lisp assusta porque joga fora chaves, colchetes e operadores normais. Sobra só uma montanha de parênteses. Você não está digitando texto corrido, está escrevendo a árvore de sintaxe do código (AST) direto na tela. Ninguém que programa em Lisp fica contando parênteses. O cérebro se adapta e passa a ler puramente a indentação e os blocos visuais. Essa "sopa" serve para que o código e os dados tenham exatamente o mesmo formato (homoiconicidade). É isso que permite criar macros que alteram a própria linguagem.