News for hackers. Stuff that matters.
Re:We need to talk about parentheses by phoe-krk on Monday February 12, 2024 @06:32AM (#39343743)
> humans can’t immediately parse structure with a glance, and sometimes we have to count parentheses by hand.

This is why automatic indentation is important when working with Lisp. This is mentioned later in the article, but I consider it mandatory because of the multitude of problem this approach resolves.

For example, upon pasting the below snippet into emacs:

    (let* ((foo 10)
           (bar (+ foo 32))
      (* foo bar))
The editor 1) notifies me that there is a missing paren somewhere, 2) automatically* reindents this code into:

    (let* ((foo 10)
           (bar (+ foo 32))
           (* foo bar))
At this moment, it's obvious that we have three variable bindings, one of them malformed, and the programmer can correct this by planting a missing paren in the proper place. I wrote about this topic at length in https://mov.im/blog/phoe%40movim.eu/cd3577f6-fb1d-45f5-b881-... before.

* with aggressive-indent and electric-indent enabled in emacs, as soon as the missing paren is placed at the end

Re:We need to talk about parentheses by elwell on Monday February 12, 2024 @01:05PM (#39348153)
And for that added flair, don't forget: https://www.emacswiki.org/emacs/RainbowDelimiters.
Re:We need to talk about parentheses by bloopernova on Monday February 12, 2024 @01:15PM (#39348274)
Aren't there 11 parentheses in both of your code snippets? I must be missing something, sorry could you possibly explain where the missing parenthesis should be?
Re:We need to talk about parentheses by taeric on Monday February 12, 2024 @01:29PM (#39348460)
It helps to basically vocalize what the code is doing. It is a "let" form, which has a list of variables, and then a body section using them. (It is a "let*", which also means that the variables can refer to variables that come before them, but not too relevant here.)

To that end, the first () in a "let" should be of the forms (name value), where value can be a longer form, if needed.

To that end, the last (name value) in the list should almost always look like (name value)), as it is closing the first in the list, that is ((name value).

Re:We need to talk about parentheses by phoe-krk on Monday February 12, 2024 @01:49PM (#39348695)

    (let* ((foo 10)
           (bar (+ foo 32)))
      (* foo bar))
Or, to demonstrate the ugly "nailclip" style:

    (let* ((foo 10)
           (bar (+ foo 32)))
      (* foo bar)
      )
Note that I moved a closing paren off the third line and onto the second one, to close the variable bindings in the LET* form.
Re:We need to talk about parentheses by bloopernova on Monday February 12, 2024 @01:54PM (#39348750)
That explains it, thank you!
Re:We need to talk about parentheses by lelandbatey on Monday February 12, 2024 @01:44PM (#39348643)
They're not saying that the second snippet is "fixed", they're saying that the auto-indented second snippet highlights the mistake to the reader. Yes there are 11 parentheses in both the code snippets because both are "incorrect" in that they're both missing a parenthesis. The GP is trying to point out that since Emacs automatically re-indents the first snippet so that it looks like the second snippet, the reader knows immediately that something "isn't quite right".
Re:We need to talk about parentheses by bloopernova on Monday February 12, 2024 @01:52PM (#39348737)
Ahhh, understood. Thank you!
Re:We need to talk about parentheses by adrian_b on Monday February 12, 2024 @02:06PM (#39348873)
Automatic indentation helps, but the main problem is that LISP uses a single kind of parentheses in all places where some kind of brackets is required.

If different kinds of brackets had been used for every distinct purpose, then there would have been no need to count parentheses or be dependent on enforcing invariable indentation rules.

ALGOL 68 is another example of programming language with fully parenthesized non-ambiguous syntax, but it uses many kinds of different bracket pairs, which makes it much easier to read. While some of its bracket pairs, like if/fi, do/od, case/esac, have been rightfully mocked, they achieve well their purpose.

In a programming language there are many different purposes for which bracket pairs are needed, e.g. for delimiting sub-expressions in order to change the default order of evaluation, for delimiting function argument lists, for delimiting array index lists, for delimiting blocks, for delimiting function bodies, for delimiting record/structure component definitions, for delimiting the components of various program structures, like conditional expressions and loops, and so on.

For an automatic parser, the use of different kinds of brackets can only make its task easier, never harder, because in the worst case its lexical analysis could replace all kinds of brackets with a single kind, while for a human reader different kinds of brackets are always helpful.

Using many kinds of bracket pairs is not necessarily as verbose as in Algol 68, because Unicode offers enough bracket pairs to choose for all purposes.

In LISP all parentheses look the same, but a LISP programmer who does not recognize that the things delimited in those parentheses are of different natures, e.g. "(CAR ..." vs. "(COND ..." vs. "(QUOTE ..." and so on, cannot write high quality LISP programs.

Many years ago, I have written many rather big Scheme or LISP programs, because they have many advantages beyond the uniform parentheses. If I would use again a language of the LISP family, I would use a text preprocessor to pass the source to the compiler, which would allow me to use various kinds of bracket pairs, for instance Unicode mathematical angle brackets for COND, Unicode bag delimiters for loops, curly brackets for blocks (curly bracket is the Unicode term for braces), and so on.

Re:We need to talk about parentheses by alexisread on Monday February 12, 2024 @03:14PM (#39349824)
I've often wondered if it was worth a language having interchangeable brackets that you could use similar to 4-colour problems eg. ([1 2] 3) is the same as {[1 2] 3} or ((1 2) 3) semantically, just visually easier to read.

Any takers?

Re:We need to talk about parentheses by adrian_b on Monday February 12, 2024 @03:32PM (#39350072)
This is more or less like the traditional use of parentheses, square brackets and curly brackets in English texts, where they had the same meaning but they were used for different levels of nested brackets.

IIRC, I have seen once an experimental language which worked exactly like in your proposal, but it was long ago and I do not remember which was it.

One LISP system that I have used long ago had a different approach. In it, one closing square bracket could be used to close all non-closed parentheses preceding it.

Re:We need to talk about parentheses by lispm on Tuesday February 13, 2024 @02:01AM (#39355297)
> One LISP system that I have used long ago had a different approach. In it, one closing square bracket could be used to close all non-closed parentheses preceding it.

That was Interlisp, originally making handling of punch cards easier. Nowadays it's not necessary, since the 70s/80s text editors on terminals support handling nested expressions.

Re:We need to talk about parentheses by joseluis on Tuesday February 13, 2024 @09:21AM (#39357843)
Rust macros can be used like that:

let hello = dbg!{ dbg![ dbg!("world") ] };

Re:We need to talk about parentheses by IanCal on Monday February 12, 2024 @03:34PM (#39350097)
We have ides with different colours for different layers of brackets, and we have them with rendering different characters (>= etc).

So it feels like this could be a reasonable plugin?

Re:We need to talk about parentheses by Gibbon1 on Monday February 12, 2024 @03:58PM (#39350400)
You could have three slightly different brackets, one for each level of nesting. And when you get to four that's it. More than that is illegal.
Re:We need to talk about parentheses by mikhayahu on Monday February 12, 2024 @05:30PM (#39351420)
Racket works this way.
Re:We need to talk about parentheses by lispm on Tuesday February 13, 2024 @01:59AM (#39355281)
> In LISP all parentheses look the same,

    (car (first a))

    (cond ((> a 10) 1)
          ((> b 20) 2)
          ((> c 30) 3))
Above looks different for me. As a Lisp programmer I don't look for parentheses, but for patterns with a starting symbol. Remember: list forms typically begin with a symbol.

> If different kinds of brackets had been used for every distinct purpose, then there would have been no need to count parentheses or be dependent on enforcing invariable indentation rules.

No Lisp developer counts parentheses. Indentation rules are used in many programming languages. Lisp applies these rules using tools, like many programming environments do.

Re:We need to talk about parentheses by jacknews on Monday February 12, 2024 @04:35PM (#39350846)
Yes, this is my argument against significant-whitespace languages like python, or even worse for simple nested data like yaml.

Actual delimiters explicitly structure your code, and then automatic formatting confirms it, kind of like double-entry book-keeping.

For me, just a couple of days with lisp and parentheses made me wish every language had such simple explicit syntax, I really recoil at some language's special syntax, eg python list comprehension. Maybe I just hate python, lol.

Re:We need to talk about parentheses by somewhereoutth on Monday February 12, 2024 @05:22PM (#39351349)

  ~ let* 
    ~ ~ foo 10
      ~ bar (+ foo 32)
      ~ * foo bar