[HN Gopher] We need to talk about parentheses (2020)
       ___________________________________________________________________
        
       We need to talk about parentheses (2020)
        
       Author : precompute
       Score  : 75 points
       Date   : 2024-02-12 07:16 UTC (15 hours ago)
        
 (HTM) web link (andreyor.st)
 (TXT) w3m dump (andreyor.st)
        
       | boxed wrote:
       | > It depends... on operator precedence
       | 
       | I think implementing operator precedence in ANY programming
       | language is a mistake. `1 + 1 * 2` should be a syntax error. In
       | C, in Python, in Java.
        
         | tempodox wrote:
         | I vigorously disagree.
        
           | lionkor wrote:
           | For that example, I agree, but how about                   a
           | | b || c >> d & e * f
           | 
           | ? I think its reasonable to say that a language should not
           | allow that
        
             | OJFord wrote:
             | You can take any reasonable grammar and come up with
             | unreasonably ugly/illegible code.
        
               | lionkor wrote:
               | Yeah, of course, but grammar can be designed as to not
               | allow as much implicit magic.
               | 
               | If you can do it, someone will do it (in prod, in your
               | product).
        
             | tempodox wrote:
             | The reason why this expression shouldn't compile has
             | nothing to do with precedences (C has some odd choices here
             | to be sure), but because of mixing incompatible types.
             | Arithmetic integers, bitvector integers, Booleans, and
             | possibly even floats. Conversions to appropriate types
             | should have to be explicit. But C's type system is weak as
             | all hell and this shit actually compiles there. (I'm only
             | mentioning C because your example looks like it.)
        
               | rfl890 wrote:
               | There's no implicit type conversion here. As long as all
               | the vars are the same type it works (albeit unpredictable
               | if you don't know operator precedence for the lang).
        
             | bmacho wrote:
             | How about *paste Linux source code here*? Should the
             | language C allow it? It's long and complicated, you don't
             | even have enough time to read it, let alone to comprehend
             | it.
        
             | samatman wrote:
             | That expression would not pass code review around me, and a
             | linter should flag it for sure.
             | 
             | But all the precedence rules you're invoking here are
             | useful in isolation, and I see no reason why they shouldn't
             | parse in combination. A linter can use heuristics to flag
             | cases which should be parenthesized, a parser would need a
             | defensible rule for failing a parse. What would that rule
             | be?
        
         | sokoloff wrote:
         | There is value in notational simplicity.
         | 
         | For the same reason that math has defined precedence (and even
         | omits multiplication signs entirely in many places),
         | programming languages in some domains will choose to do the
         | same.
         | 
         | It's not a problem if you want to create a language that does
         | not do this, but I think it is a problem if you want to ban
         | others from doing it.
        
           | tsimionescu wrote:
           | Algebra only really has defined precedence between
           | multiplication and addition/subtraction. In all other cases,
           | even division, there is either a non-linear textual
           | representation, or parantheses are required. You have
           | fractions, exponents, radical signs, various signs which
           | appear only at the beginning of an expression (sigma,
           | integral, lim) etc.
           | 
           | Even multiplication is often distinguished more by the shape
           | of the text than by actual precedence rules, once you leave
           | basic arithmetic - expressions like "1 - 3xy + 2y2" make the
           | order of operations obvious from text layout, which is why
           | they are preferred over "1 - 3 x x x y + 2 x y ^ 2", which no
           | mathematician ever writes.
        
             | sokoloff wrote:
             | Exponentiation also has defined precedence, does it not?
             | (Everyone who has passed algebra agrees that y is squared
             | before being multiplied by 2 in your example.)
        
               | tsimionescu wrote:
               | You can view that as precedence, but I think it's more
               | correct to say that exponentiation binds to the single
               | symbol it is attached to. When you write x2 + 2, I don't
               | think it's operator precedence that makes it clear this
               | is not equal to x ^ 4.
               | 
               | Similarly, in the following expression:                 x
               | _ + 2       2
               | 
               | I don't think it's precedence that makes it clear you
               | first divide by 2 and then add 2 to the result.
        
               | sokoloff wrote:
               | Alternatively, perhaps what you're observing is simply
               | the graphical representation of the underlying
               | mathematical precedence rules. (Obviously, over hundreds
               | of years of symbolic math, the notation and precedence
               | has co-evolved, with precedence probably generally, well,
               | preceding notation.)                 y = x / 3 + 2
               | y = 2 + x / 3            y = x/3 + 2
               | 
               | We all agree those are the same, despite a lack of an any
               | cues in the first two. That suggests to me that there is
               | a defined precedence for division vs addition in math.
               | (So much so that it doesn't even seem like a
               | controversial statement to make.)
        
               | tsimionescu wrote:
               | Precedence rules always apply to a specific textual
               | notation. There is no precedence between division and
               | addition, there is a precedence between the / and the +
               | operators.
               | 
               | People are generally bad at remembering arbitrary
               | operator precedence rules. That is why most math notation
               | doesn't rely on precedence rules to be unambiguous, it
               | uses other kinds of textual clues. For example, the /
               | operator is virtually never used in algebra. Instead,
               | division is indicated by fractions, which explicitly
               | group their operands and separate them from other
               | adjacent operations. Exponentiation relies on super
               | scripts to separate its arguments, making it extremely
               | unambiguous at least which operations are part of the
               | exponent and which the total. Roots use the radical
               | symbol which extends over the entire expression you're
               | taking the root of, and again rely on a kind of
               | superscript for indicating which root you are taking.
               | Limits and sums use subscripts to separate the iteration
               | direction from the expression. Integrals use both sub and
               | superscripts.
        
               | sokoloff wrote:
               | > Exponentiation relies on super scripts to separate its
               | arguments, making it extremely unambiguous at least which
               | operations are part of the exponent and which the total.
               | 
               | The textual representation even for exponents is not
               | enough if you don't already know that exponentiation has
               | higher precedence than multiplication.
               | 
               | "1 - 3xy + 2xy2"
               | 
               | Both of us know that only the y term is squared in that
               | expression. How do we know that?
               | 
               | I claim that we know that because we know the precedence
               | rules and that someone never exposed to our system of
               | algebra would not inherently and unambiguously know that
               | it was 2*x*(y2) rather than (2*x*y)2 or even 2*(x*y)2
               | merely by visual inspection of the expression.
               | 
               | This (the meaning of the above expression, including the
               | precedence/grouping) is something that has to be taught
               | in Alegbra/Prealgebra courses, and is something that some
               | students struggle with.
        
               | tsimionescu wrote:
               | For expnentiation I was talking about something else:
               | 1+3       x    + 4
               | 
               | In this case, the exponent notation makes it unambiguous
               | that this is (x^(1+3))+4, and not x^(1+3+4).
               | 
               | I agree that xy2 could mean both (xy)2 and x(y2), and
               | that perhaps you need to understand precedence to
               | disbiguate. I would still contend that the notation is
               | defined as "each term has its own exponent, possibly 1
               | which need not be written" to disambiguate, where terms
               | are separated by parentheses or by other operators.
               | 
               | That is, in my mind, the grammar for usual algebraic
               | notation is something like this:
               | expression = term (op term)*
               | expression___________ (expression)?        term = (num |
               | var | fraction | \( expression \) |        \/expression)
               | op = + | - | / | x | deg etc
               | 
               | And the rules are that you first evaluate each term, and
               | then the operators between the terms. You only need to
               | follow precedence rules for those operators between
               | terms, the evaluation of a term is unambiguous.
        
               | samatman wrote:
               | You appear to be inventing elaborate ways to talk about
               | precedence without saying "precedence".
               | 
               | Precedence, under the term "order of operations",
               | predates computers by several centuries. Which is not to
               | apply that it was fixed in stone from the beginning, or
               | perfectly consistent, but mathematics agreed on the
               | modern rules universally in the first couple decades of
               | the 20th century.
        
               | thaumasiotes wrote:
               | > Alternatively, perhaps what you're observing is simply
               | the graphical representation of the underlying
               | mathematical precedence rules.
               | 
               | This is nonsense. There are no underlying mathematical
               | precedence rules. You're seeing precedence being
               | represented visually, but there is no mathematical
               | difference between 5 3 ADD 2 MUL and 5 3 2 MUL ADD.
               | 
               | MUL and ADD are just binary operators, or if you prefer,
               | functions from U x U to U for some set U.
               | 
               | Your statement is _particularly_ nonsensical as applied
               | to the example you 're supposedly responding to. Given
               | some expressions:                   5 + 3     5
               | 5        ------- ; --- + 3 ; -------           2       2
               | 2 + 3
               | 
               | How do you even form the argument that the difference in
               | interpretation is "simply the graphical interpretation of
               | the underlying precedence rules"? The precedence rules
               | here are obvious, because they don't exist:
               | 
               | - Division occurs between the upper operand and the lower
               | operand
               | 
               | - Addition occurs between the left operand and the right
               | operand
               | 
               | It's impossible for these to conflict, so there is no
               | such concept as precedence.
               | 
               | (Precedence also doesn't exist between addition and
               | addition because addition is associative. It is necessary
               | between division and division, and the system is
               | straightforward: lower precedence is indicated by longer
               | lines. Tell me about the underlying mathematical
               | precedence rule that determines that.)
        
               | sokoloff wrote:
               | > There are no underlying mathematical precedence rules.
               | 
               | You might want to correct the information here then:
               | 
               | https://en.wikipedia.org/wiki/Order_of_operations#Convent
               | ion...
        
               | thaumasiotes wrote:
               | What do you think I'd want to correct? That page says the
               | same thing you've already been told twice: there is no
               | underlying mathematics, and the meaning of the notation
               | is defined by the notation. Here's a quote:
               | 
               | > These rules are meaningful only when the usual notation
               | (called infix notation) is used. When functional or
               | Polish notation are used for all operations, the order of
               | operations results from the notation itself.
               | 
               | You could "correct" that to observe that "the order of
               | operations results from the notation itself" is true of
               | all notation, including infix notation, but why? It's not
               | wrong now.
               | 
               | Can you really not tell the difference between "notation"
               | and "math"?
        
               | samatman wrote:
               | "Order of operations" is the _order_ in which operations
               | _proceed_. Hence _precedence_.
               | 
               | They aren't a fact about mathematics. They're a fact
               | about _notation_. Operator precedence and order of
               | operations are synonymous.
        
           | lex-lightning wrote:
           | Yes, but programming is more akin to copywriting than
           | mathematical notation most of the time.
           | 
           | Math has very well-defined, well-trod conventions that are
           | _densely_ packed into the notation. When people try to write
           | code like this though, it's hard to read.
           | 
           | All else equal, I'll take a snippet of code with more
           | characters in it but less work to unpack those characters.
           | Reading linearly is cheap; I get tired reading formulas
           | faster than reading technical prose. Precedence makes the
           | mental parsing nonlinear.
        
           | dheera wrote:
           | The REAL problem comes from math and physics teachers (even
           | at the university level) writing things like:
           | 
           | (a + b) / 2p
           | 
           | instead of
           | 
           | (a + b) / (2p)
           | 
           | It seems that math teachers put division above multiplication
           | in order of operations but programming languages almost
           | universally treat division and multiplication as equal and
           | read them left-to-right. I've fixed multiple bugs before that
           | were due to this.
        
         | lifthrasiir wrote:
         | Some operator precedences are way more reasonable than others,
         | and those between arithmetic operators are good examples. What
         | we actually need is a partial order of operator precedences:
         | you can have `1 * 2 + 3` or `full_name ?? first_name ++ " " ++
         | last_name` but not necessarily `1 * 2 & 3` because that
         | combination of operators is uncommon and a parenthesis should
         | be preferred instead of an arbitrary total order.
        
         | zarzavat wrote:
         | Should `x == 1 or y == 3` be a syntax error? Because that's
         | operator precedence too.
        
           | kragen wrote:
           | that's certainly a plausible position; in lisp (or = x 1 = y
           | 3) is generally an error; even if it's not an error it
           | doesn't mean what you intend it to. having to write (x == 1)
           | || (y == 3), similar analogous to the code you'd write in
           | lisp, would be only a minor burden
           | 
           | in c, x == 1 || y == 3 works fine, but unfortunately the
           | bitwise operators have the wrong precedence; x & 2 == 2 is
           | parsed as x & (2 == 2), which is never what you meant. so
           | even the best software designers have made terrible blunders
           | in precedence design
           | 
           | math formulas are a bigger issue; 3*x**2 + x/2 + 1 is a lot
           | better than the fully parenthesized version
        
             | zarzavat wrote:
             | There are of course operations with unclear precedence
             | relationships and it's completely reasonable to want to
             | force parentheses when composing those operations.
             | 
             | However, there are also operations with crystal clear
             | precedence relationships, such as logical conjunction and
             | comparison, and in that case it has to be asked what
             | exactly would the parentheses be there for?
             | 
             | In lisp the answer is: to enable syntactic homogeneity and
             | to enable macros.
             | 
             | But in other languages like Python that don't have sexpr
             | based syntax, parenthesizing everything would just be
             | pointlessly unreadable.
        
           | ctrw wrote:
           | That is type inference actually.
        
         | frud wrote:
         | For basically forever compilers and languages have been
         | designed with a total ordering on operator precedences. The
         | Yacc compiler generator tool maps operator precedence to
         | integers, as do most handwritten compiler parsers.
         | 
         | A total ordering has a definite answer to the question "is x
         | greater than, equal to, or less than y?" for all x and y. A
         | partial ordering will answer "I don't know" for some x and y.
         | 
         | It's quite possible to implement operator precedence using a
         | partial ordering. When the parser has to resolve precedence
         | between two operators and their relationship is not defined,
         | throw an "ambiguous precedence" error.
         | 
         | You can implement the partial ordering by putting all the
         | operators into a DAG of sets of operators. If two operators are
         | in the same set, they have equal precedence. If there is a path
         | through the DAG from one operator to the other, that defines
         | the precedence relation. Else there is no relation.
         | 
         | Say "*" is defined to have a higher precedence than "+", and
         | they both have an undefined precedence relation with "&". Then
         | "1 + 2 * 3" should compile into "1 + (2*3)", but "1 + 2 & 3"
         | should throw an "ambiguous precedence" syntax error.
        
         | adrian_b wrote:
         | The lack of operator precedence has no relationship with the
         | mandatory use of parentheses.
         | 
         | For example the APL syntax for expressions, which I prefer, is
         | that there is no operator precedence.
         | 
         | The operands are associated to operators strictly from the
         | right to the left, regardless which operators or functions are
         | used, which is a much wiser choice than the reverse traditional
         | order of evaluation.
         | 
         | No parentheses are used, except when they are needed to modify
         | the default evaluation order.
        
         | samatman wrote:
         | That would get really annoying.
         | 
         | Not in expressions like 1 + 3 * 6. Personally, I include the
         | parentheses anyway, as a matter of style, and would write 1 +
         | (3 * 6).
         | 
         | But in expressions like i + 3 > 4 / n? No thank you, (i + 3) >
         | (4 / n) would drive me up a wall. Ymmv.
        
       | phoe-krk wrote:
       | _> 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
        
         | elwell wrote:
         | And for that added flair, don't forget:
         | https://www.emacswiki.org/emacs/RainbowDelimiters.
        
         | bloopernova wrote:
         | 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?
        
           | taeric wrote:
           | 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).
        
           | lelandbatey wrote:
           | 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".
        
             | bloopernova wrote:
             | Ahhh, understood. Thank you!
        
           | phoe-krk wrote:
           | (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.
        
             | bloopernova wrote:
             | That explains it, thank you!
        
         | adrian_b wrote:
         | 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.
        
           | alexisread wrote:
           | 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?
        
             | adrian_b wrote:
             | 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.
        
             | IanCal wrote:
             | 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?
        
             | Gibbon1 wrote:
             | You could have three slightly different brackets, one for
             | each level of nesting. And when you get to four that's it.
             | More than three is illegal.
             | 
             | Edit: Or you could use markup to make the text size smaller
             | and smaller.
        
             | mikhayahu wrote:
             | Racket works this way.
        
         | jacknews wrote:
         | 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.
        
         | somewhereoutth wrote:
         | ~ let*          ~ ~ foo 10           ~ bar (+ foo 32)
         | ~ * foo bar
        
       | tromp wrote:
       | > Here's the same code, but with all parentheses in place:
       | (define (fac x)           (if (<= x 0) 1               (* x
       | fac (- x 1))))
       | 
       | Still one pair missing.
       | 
       | Is it true that in normally formatted LISP code, every line
       | should start with an opening parenthesis?
       | 
       | Btw, I prefer Haskell's syntax of                   fac x = if x
       | <= 0 then 1 else x * fac (x-1)
        
         | mtreis86 wrote:
         | Nope, lisp doesn't care about whitespace at all, including line
         | endings. The way lisp is read in is as if there was no
         | whitespace, just objects in lists.                 (foo bar
         | baz)            (foo       bar baz)            (foo       bar
         | baz)
         | 
         | All the same thing to lisp
        
           | tsimionescu wrote:
           | That's not quite right. Lisps care a lot about the _presence_
           | of whitespace, just not about their type or number. (foo bar
           | baz) is very very different from (foobarbaz). They are
           | identical in this respect to the C family of languages, in
           | fact (the only difference is that they allow newlines in
           | quoted strings, which C languages normally do not).
        
             | samatman wrote:
             | The term of art is _whitespace insensitive_ , meaning that
             | whitespace is only necessary when needed to separate
             | tokens, and any amount will do when it's allowed. While it
             | may be possible to write a language in which whitespace may
             | be inserted or left out literally anywhere, I'm unaware of
             | any actual languages where this is true.
        
       | KingOfCoders wrote:
       | Coming from Java, Scala, Javascript one nice benefit of Go, was
       | no parantheses around if conditions. At first I thought an
       | irrelevant change but now I do think it makes code more readable.
        
         | xmcqdpt2 wrote:
         | Probably one of the best thing about the new "pythonized" Scala
         | 3 syntax is that it doesn't require parentheses around if
         | conditions.
        
       | wlindley wrote:
       | >>> ... "curly braces" ... << Ugh. Braces _are_ "curly" -- there
       | are no "square braces" or "round braces." Needlessly redundant
       | and confusion-inducing. Can we please eradicate this
       | circumlocution from programming language?
        
         | _Wintermute wrote:
         | This is a regional thing. In the UK they're all brackets.
         | 
         | Brackets, square brackets, curly brackets.
        
         | tsimionescu wrote:
         | They are also called curly brackets, so I imagine the confusion
         | comes from there, and from the fact that the words "braces" and
         | "brackets" are relatively similar, making them easy to confuse,
         | especially for non-natives.
        
         | ehecatl42 wrote:
         | Parentheses (()), braces({}), brackets([]), and chevrons(<>). I
         | concur.
        
           | tsimionescu wrote:
           | Or, in British English, brackets(()), curly brackets({}),
           | square brackets ([]).
        
             | iainmerrick wrote:
             | And I've heard both "angle brackets" and "pointy brackets"
             | (<>)
        
               | lex-lightning wrote:
               | Smooth, squiggly, hard, and pointy are my gotos
        
           | PeterisP wrote:
           | That would be fine if we could somehow magically change the
           | English language so that this would universally match how
           | people call these symbols around the world - but that's not
           | likely to happen.
        
           | teddyh wrote:
           | <http://www.catb.org/~esr/jargon/html/A/ASCII.html>
        
         | codingdave wrote:
         | Considering the worldwide population of coders don't all share
         | a common native language, no - we need to embrace fluidity in
         | our vocabulary.
        
       | oakpond wrote:
       | Maybe the outcome of this syntax experiment is that less is not
       | always more. Maybe in general people prefer more syntactical
       | notations than what Lisps offer out of the box.
        
         | zelphirkalt wrote:
         | Also many people, who have actually used Lisps prefer the
         | notation for its ease of editing code.
        
       | kragen wrote:
       | programmers a choosing a language because of syntax are like
       | house buyers choosing a house because of the color it's painted
        
         | lex-lightning wrote:
         | But what if the two houses are otherwise identical? Even a
         | small convenience will tip decisionmaking
        
           | kragen wrote:
           | in cases similar to that, it would be a reasonable thing to
           | do
        
         | tmtvl wrote:
         | I'd like to think I'd have a far easier time painting a mauve
         | house ochre than I'd have trying to change Rust to have an
         | S-expression syntax (not in the least because I have the
         | programming chops of a slightly dimwitted housefly).
        
         | erik_seaberg wrote:
         | The wrong paint won't stop you from adding a garage, but
         | choosing a complex and non-uniform syntax makes it so hard for
         | end users to extend the language that we almost never try.
         | Imagine how much more readable Java would be if Lombok didn't
         | need to smuggle each option in a prefix @Annotation.
        
         | samatman wrote:
         | I seem to recall some fellow or other describing notation as a
         | tool of thought.
         | 
         | That doesn't sound superficial to me.
        
       | bmacho wrote:
       | I don't think we need to talk about parentheses?
       | 
       | I doubt there is anything nontrivial but useful to say about
       | them?
       | 
       | Anyone found anything in the article?
        
       | Joker_vD wrote:
       | YALPAAG (Yet Another "LISP's Parentheses Are Actually Great")
       | article.
       | 
       | One would think that if they _really_ were that great, there
       | wouldn 't have been any need to keep writing such articles after
       | several years, and yet here we are again.
       | 
       | > humans can't immediately parse structure with a glance
       | 
       | No, humans absolutely can, as long as those structures are: a)
       | delimited; and b) the delimiters used aren't nauseatingly
       | repeating. LISP's parentheses start failing at the b) condition
       | much sooner than semantic whitespace/indentation starts to.
        
         | nataliste wrote:
         | YAEAR (Yet Another "Earth's Actually Round") article.
         | 
         | One would think that if the earth was _really_ round, there
         | wouldn 't have been any need to keep writing such articles
         | after several years, and yet here we are again.
         | 
         | > humans can't immediately see curvature with a glance
         | 
         | No, humans absolutely can, as long as those curvatures are: a)
         | intuitive; and b) the curvatures used aren't nauseatingly
         | large. Round Earth start failing at the b) condition much
         | sooner than a flat-earth model.
         | 
         | In short, generations are forced to effectively re-learn basic
         | facts about the world they inhabit because new members are in
         | fact ignorant and must be disabused of their intuitive yet
         | incorrect assumptions.
        
           | samatman wrote:
           | What on Earth did you intend to convey with this? I'm
           | mystified.
           | 
           | Are you, in fact, comparing a preference for Algolic syntax
           | over s-exprs, to... flat Earth theory?
           | 
           | Surely not.
        
       | xmcqdpt2 wrote:
       | > And this is, in fact, a made-up example but the thing is, I was
       | a C programmer for the past 5 years, and I was working with low-
       | level code (bare metal actually). And I have never seen the use
       | of explicit scoping of variables. [...] I find this a major
       | problem when dealing with C because I always see about 10
       | uninitialized variables at the start of each function, where half
       | of those are used only once. Somewhere. And thank god, if there
       | are no global variables.
       | 
       | Sure, if you write C like it's 1995, it's not going to be very
       | readable. You don't need to define variable at the beginning of a
       | scope, and you can use {} to scope variables to tighter blocks.
       | And functions can be small.
       | 
       | (Sometimes you need to keep variables for longer than you would
       | like for cleanup reason, that's true.)
        
         | ctrw wrote:
         | So your solution to having too many () is to have just as many
         | {}. I'm not complaining but...
        
           | PeterisP wrote:
           | Well, no, the above solution is to have a {} _if and only if
           | explicit scoping is desired_ instead of having just as many
           | {} as there would be ().
           | 
           | Sometimes you need explicit scope for a bunch of things and
           | the code would benefit from it. That doesn't justify having
           | explicit scopes for everything all the time.
        
             | zelphirkalt wrote:
             | Isn't is always safer to have variables scoped only to the
             | relevant part of the code, that is supposed to make use of
             | them? So basically it would result in almost the same
             | number of parens.
        
               | lex-lightning wrote:
               | This isn't always possible, even in lisp. Compare to
               | Rust's non-lexical lifetimes
        
               | zelphirkalt wrote:
               | I don't see how it could ever be not possible. Maybe not
               | always convenient, but impossible? How?
        
               | lex-lightning wrote:
               | a = getInt(); b = getInt() + a; c = getInt(); d = b / c;
               | puts(d); puts(c); puts(b); puts(a);
               | 
               | Nothing in the middle of this code prevents using 'a'
               | even though it isn't relevant to 'c'. And there's no way
               | to arrange it to change that.
        
               | zelphirkalt wrote:
               | If `c` does not depend on `a`, then the re-arrangement
               | seems simple: Define `c` later.
        
               | susam wrote:
               | > If `c` does not depend on `a`, then the re-arrangement
               | seems simple: Define `c` later.
               | 
               | How does that prevent 'a' from being in the scope where
               | 'c' is defined? It is not clear to me what you mean by
               | defining 'c' later. Later where? And how do we ensure
               | that 'a' is not accessible there?
               | 
               | I think lex-lightning's comment has a point. We need to
               | define 'a' and we need to define 'c' and then we need to
               | print 'c' _before_ printing  'a', so although 'c' does
               | not depend on 'a', we still need to keep 'a' in scope
               | because we cannot print it before we print 'c'.
               | 
               | Perhaps a convoluted solution to the problem posed in
               | lex-lightning's comment would be something like this:
               | #include <stdio.h>            int getint() {
               | return 42;       }            int main()       {
               | int a = getint();           int b = getint() + a;
               | int c;                {               // Shadow 'a' here
               | to make the outer 'a' inaccessible in this scope.
               | int *a = NULL;               (void) a;               c =
               | getint();           }                int d = b / c;
               | printf("%d\n", d);           printf("%d\n", c);
               | printf("%d\n", b);           printf("%d\n", a);
               | return 0;       }
               | 
               | Output:                 $ cc -std=c99 -Wall -Wextra
               | -pedantic foo.c && ./a.out       2       42       84
               | 42
        
               | lex-lightning wrote:
               | I appreciate your interest.
               | 
               | I'd argue that even declaring 'c' is against the spirit
               | though. To say nothing of the eldritch shadow cast there
               | (no disrespect, I think we're both enjoying playing
               | around here)
        
               | susam wrote:
               | > I'd argue that even declaring 'c' is against the spirit
               | though.
               | 
               | Yes, I agree. My code example is meant to show the best
               | we can possibly do to solve the problem in your comment
               | and how contrived that solution is. The fact that this
               | solution is contrived and diverges from the intended
               | spirit of the problem only emphasizes the point of your
               | comment.
               | 
               | That's why I am eager to understand what zelphirkalt
               | really meant when they wrote, "the re-arrangement seems
               | simple: define `c` later". It seems far from simple and,
               | in fact, impossible if we want to avoid contrived
               | solutions.
        
               | zelphirkalt wrote:
               | I see now how the problem is. Thanks. I have a suspicion,
               | that this depends on side effects and that without
               | assignments and without side effects things might look
               | different. Somehow I don't seem to come across these
               | scenarios, when I write FP programs.
        
               | lex-lightning wrote:
               | Hey I'm down to party with some monads, but I leave that
               | as an exercise to the reader
        
         | lex-lightning wrote:
         | The drawback of the microscopic function craze is you lose the
         | ability to read a whole procedure from top to bottom. If some
         | code is truly reused, break it out into a function with a good
         | name, sure.
         | 
         | But in my domain I have seen countless one-line functions used
         | in one place each, and it's frustrating having to jump around
         | the code (and add to my mental context stack*) rather than just
         | putting some curly braces and a comment. Functions aren't
         | primarily meant as a hygiene mechanism. I really like the curly
         | brace scoping in Rust.
         | 
         | I found my style partly by reading John Carmack and Casey
         | Muratori's coding philosophies and putting them to the test for
         | myself.
         | 
         | * For a natural language analogy, consider the difference
         | between parenthetical remarks and asterisks. You have to
         | remember your place in the text.
        
           | jolt42 wrote:
           | I define functions as often in let/letfn blocks than I do as
           | separate functions, so they are local and in context. Makes
           | it easy to read.
        
             | lex-lightning wrote:
             | This sounds like another option while maintaining agreement
             | with the gist of what I meant. Those are good, though
             | that's where I start preferring smallness
        
           | agentultra wrote:
           | It's a personal preference based on how we mentally model
           | problems. I cannot stand long procedural code. The mental
           | gymnastics required to know what it does is too much. More
           | than 5-6 variables in scope and I have no idea if it does
           | what you say it does. It would take me some time to figure
           | that out. If it is under test we can at least say it's not
           | wrong some times.
           | 
           | I much prefer breaking problems down to the level where I can
           | reason in terms of sets, algebras, and that sort of thing.
           | It's much easier for my brain to rely on properties and the
           | ability to substitute terms in equations than it is to
           | reconstruct a giant state machine and execute it in my head.
           | Much more so when we add concurrency to the mix.
           | 
           | At the end of the day though I don't think there's, "one true
           | way."
        
             | lex-lightning wrote:
             | When I was a kid I would tell my parents I cleaned my room,
             | then they'd look in the closet and see everything in a
             | pile.
        
               | pimlottc wrote:
               | Don't let perfect be the enemy of good
        
           | nyrikki wrote:
           | Mostly a problem with people who borrow their concepts of
           | cohesion from MVC, but yes people tend to fundamentally
           | misunderstand SIP and it results in over modularity.
           | 
           | IMHO that is a side effect of how we teach people how to
           | program mixed in with orgs cargo culting acronyms and not the
           | core concepts of ideas.
        
             | lex-lightning wrote:
             | For real though. This was my biggest frustration when I was
             | an SE.
             | 
             | Want people in a large organization to ignore your ideas?
             | Speak them out loud.
             | 
             | Want them to follow them as dogma? Write a blog post under
             | a pseudonym.
        
           | mkehrt wrote:
           | You should be able to read a function from top to bottom and
           | it should show you the whole procedure _at a given level of
           | abstraction_. Hiding the details is the heart of what
           | programming is.
           | 
           | I always try to write code like
           | 
           | fn foo() {                   let foo = do_first_thing();
           | let bar = do_second_thing();              do_third_thing(foo,
           | bar);
           | 
           | }
           | 
           | whenever possible. Then you can see the abstract overview of
           | what the code does at a glance, but you can drill down into
           | the details if you need to.
        
       | wslh wrote:
       | In a simple DSL I am working based on s-expressions I enable
       | things like removing parenthesis in single line expressions. For
       | example, (define a "hello world!") is just define a "hello
       | world!" <EOL>
       | 
       | Another example that could work more than RPN/FORTH (or close to
       | the ; operator in smalltalk) is:
       | 
       | (* 4 (+ 1 2 3))
       | 
       | equals to:
       | 
       | + 1 2 3 . <EOL>
       | 
       | * 4 <EOL>
       | 
       | The DSL is not LISP based but s-expr to define a DAG like in an
       | spredsheet.
        
       | QuadmasterXLII wrote:
       | Pssst- hey kid! I heard you came to this side of town looking for
       | a lisp with no parentheses.                   If you're not a
       | narc,              I've got the goods.              Try a little
       | bit of this julia!         end
       | 
       | Ok, ok I know your parents warned you about indexing from 1 but
       | they also thought that global variables should be replaced with
       | singleton factories, so what do they know.
        
         | klabb3 wrote:
         | Monster! You'll have to pry my 0-indexing from my cold dead
         | hands.
        
       | lispm wrote:
       | We actually need to talk about lists. The single reason why Lisp
       | uses parentheses is the list notation -> programs are actually
       | lists, not just text.
       | 
       | The same code which runs                   CL-USER 10 > (let*
       | ((foo 10)                            (bar (+ foo 32)))
       | (* foo bar))         420
       | 
       | becomes data, when quoted:                   CL-USER 11 > (quote
       | (let* ((foo 10)                              (bar (+ foo 32)))
       | (* foo bar)))         (LET* ((FOO 10) (BAR (+ FOO 32))) (* FOO
       | BAR))
       | 
       | A typical Lisp function can then substitute all * symbols to +
       | symbols:                   CL-USER 12 > (subst '+ '* '(LET* ((FOO
       | 10) (BAR (+ FOO 32))) (* FOO BAR)))         (LET* ((FOO 10) (BAR
       | (+ FOO 32))) (+ FOO BAR))
       | 
       | EVAL can execute lists:                   CL-USER 13 > (eval
       | (subst '+ '* '(LET* ((FOO 10) (BAR (+ FOO 32))) (* FOO BAR))))
       | 52
       | 
       | Thus the single main feature is that Lisp itself is an
       | application of the List Processor. This feature then is _user
       | visible_ in read eval print loops, debuggers, inspectors, code
       | formatters, source interpreters, code generators (- > macros),
       | embedded languages, ... So Lisp developers loved it, because one
       | would not need translators between user facing (infix) syntax and
       | internal data structures (ASTs, ...). Thus early attempts for
       | other syntax variants failed, because this duality between data
       | and code has some kind of elegance. Something which for example
       | impressed Alan Kay (of OOP / Smalltalk / Dynabook / ... fame),
       | when he understood that something like a Lisp evaluator can be
       | written in a few lines of Lisp code, processing lists, which is
       | code. -> https://www.quora.com/What-did-Alan-Kay-mean-by-Lisp-is-
       | the-...
        
         | taeric wrote:
         | I had fun trying to explain this a few years back.
         | https://taeric.github.io/CodeAsData.html
         | 
         | I do have to confess I have rarely taken huge advantage of this
         | fact. But the idea is pretty easy to help look at code.
        
         | kqr wrote:
         | List is confusing terminology. It's a tree. Sure, any node of a
         | Lisp tree is implemented as a list of branches, but
         | fundamentally we are talking about trees, still.
        
           | zelphirkalt wrote:
           | Would you like to call them "TREEPs"?
        
           | lispm wrote:
           | Foremost, it's just nested lists and not a tree. We are
           | talking about lists and atoms. We are then talking about list
           | operations, like getting list elements, appending lists,
           | reversing lists, listing things, nesting lists and atoms,
           | mapping lists, matching lists, destructuring of lists, ...
           | 
           | This is a list:                   (munich frankfurt dresden)
           | 
           | This is a list of lists                   ((munchen   :state
           | Bavaria)          (frankfurt :state hessen )
           | (dresden   :state sachsen))
           | 
           | If we look at the level of conses and atoms, then we see a
           | binary tree and operations: car, cdr, cons, ... Lisp is there
           | a low-level language: lists are made of simpler building
           | blocks: two-item nodes and atoms.
           | 
           | This is a tree:                   ((a . (10 . nil))
           | .          ((b . (20 . nil))           .           nil))
           | 
           | The dot is an infix syntax element for a cons cell.
           | 
           | Which as a nested list is shorter written as
           | ((a 10) (b 20))
        
         | skybrian wrote:
         | It's a shallow understanding of the code if you don't know the
         | variable definitions and types. Good enough for some things,
         | though.
        
           | lispm wrote:
           | Correct, that's where for example an interpreter walks the
           | code and builds up the understanding. Similar a compiler, a
           | code walker or other tools.
           | 
           | The shallow view allows in Lisp to provide new/different
           | views of code. That's good and bad. Many transformations of
           | code don't need a deep understanding, many do. In Lisp I can
           | write (infix 3 + 1 * sin (x)), because an infix macro can
           | provide its own interpretation, by transforming the code. But
           | then I need to parse the code and find out what the things
           | are: variables, literals, operators, calls, ...
           | 
           | For example when I want to substitute FOO with a call:
           | CL-USER 34 > (LET* ((FOO 10) (BAR (+ FOO 32)))
           | (symbol-macrolet ((foo (print 2)))
           | (* foo bar foo bar foo           ; 1-3
           | (flet ((foo (a) (* foo a)))   ; 5
           | (foo (* foo bar))))))       ; 4              2  ; 1         2
           | ; 2         2  ; 3         2  ; 4         2  ; 5
           | 2370816
           | 
           | Here the symbol-macrolet knows which FOO to expand, and which
           | not. There are five expansions in the code, which makes 2
           | printed five times. You can see that it works in nested code,
           | too : it knows that some FOO are function names and those
           | will not be expanded.
           | 
           | Whereas a MACROLET knows, where an operator needs to be
           | expanded:                   CL-USER 38 > (LET* ((FOO 10) (BAR
           | (+ FOO 32)))                        (macrolet ((foo (a)
           | `(print ,a)))                          (* foo bar foo bar
           | (foo bar)    ; 1                             (flet ((bar (a)
           | (foo a)))    ; 2                               (bar (* foo
           | bar))))))              42     ; 1         420    ; 2
           | 3111696000
        
       | danieltanfh95 wrote:
       | The weird thing about parentheses is that lispers only really get
       | how powerful and how simple it makes programming after prolonged
       | exposure, and thats why every now and then we have articles that
       | try to explain this magical moment to folks.
       | 
       | The strength of lisps syntax basically boil down to a few things:
       | 
       | 1. Scope is visually clearer than in non-lisps, you dont need to
       | hold the parser in your head while reading code.
       | 
       | 2. Code as lists allows consistent and simple macros.
       | 
       | 3. No need to reinvent editor tooling.
        
         | melagonster wrote:
         | This is a interesting idea!
         | 
         | Maybe all programming languages have their sweet points that
         | learners find "magic" and then generate blog articles.
        
       | jwindle47 wrote:
       | I loved this article, this is exactly what programmers in my
       | community complain most about with regard to lisp.
       | 
       | The greatest strengths for me are the scoping and how lisp
       | enables interactive development. For one, I don't even see parens
       | anymore when writing code. They've become automatic where I'm
       | able to parse structure at least better than I have in the past.
       | With a REPL, (I specifically use Clojure), development has become
       | fun again. A typical debugging process is attaching to my actual
       | program, and calling functions seeing what happens.
       | 
       | I love the flow of defining functions in my code, `def`-ing some
       | test data, and calling those functions. I'll use `cider-inspect`
       | to see if the data structure looks correct and iterate.
       | 
       | Everyone should at least try a lisp. Iterative, interactive
       | development is so fun and powerful.
        
       | RodgerTheGreat wrote:
       | Programming languages are user interfaces. The syntax of a
       | programming language is part of that user interface. Some choices
       | are poor by virtually any measure (stropped keywords come to
       | mind; as visually hideous as they are unpleasant to type), but
       | most choices represent tradeoffs that favor certain usage
       | patterns over others.
       | 
       | Lisp de-prioritizes legibility at a glance and concision in order
       | to give maximum priority to simplicity in metaprogramming. I'm
       | not convinced this is a good tradeoff; I would no sooner choose
       | to drive to work in an amphibious tank that got 0.2 miles per
       | gallon to be able to cut across a single river along the way.
       | 
       | Lispers say that I could use specialized tooling to compensate
       | for poor ergonomics, but in _many_ contexts- someone else 's
       | computer, a text box or static text on a website, a general-
       | purpose textual diff or search tool, print in a book, a
       | whiteboard- these affordances are not available, and I would
       | still have to contend with the inconvenient reality of Lisp's
       | _very real_ design choices.
        
         | thaumasiotes wrote:
         | > stropped keywords come to mind; as visually hideous as they
         | are unpleasant to type
         | 
         | It's pretty common today for people to voluntarily put SQL
         | keywords in all caps.
         | 
         | > Lisp de-prioritizes [...] concision
         | 
         | Really?
        
           | RodgerTheGreat wrote:
           | By the standards of a K programmer, Lisp is _quite_ verbose
           | and excessively nested.
        
           | trealira wrote:
           | > Really?
           | 
           | Yeah, Lisp can be verbose due to the lack of built-in syntax
           | for things other than lists.
           | 
           | Take the Common Lisp syntax/library functions (there's not
           | really built-in syntax) for hash tables:
           | 
           | https://cl-cookbook.sourceforge.net/hashes.html
           | 
           | Compare it with Python dictionary syntax:
           | 
           | https://developers.google.com/edu/python/dict-files
           | 
           | Almost everything that's built-in to other languages has to
           | be given a name that makes sense in Lisp. It makes it more
           | readable in some ways, but also more verbose.
        
             | NikkiA wrote:
             | alists and plists are a much closer lisp analog to python's
             | dicts though
        
               | trealira wrote:
               | That's fair. Dictionary literals aren't really any more
               | concise than a-lists.
               | 
               | A-list:                 (("the" . 50) ("be" . 25))
               | 
               | Dictionary literal:                 {"the": 50, "be": 25
               | }
        
               | RodgerTheGreat wrote:
               | Using both a-lists and p-lists as mere structural
               | conventions- and thus needing to convert back and forth
               | or write multiple versions of routines to handle each- is
               | a good example of how sexprs provide a superficial veneer
               | of simplicity while inviting cancerous complexity in
               | practice.
               | 
               | The Clojure approach of recognizing associative
               | structures as a fundamental building-block and giving
               | them specialized syntax:                   {:the 50 :be
               | 25}
               | 
               | Is a clear-cut improvement.
        
             | vindarel wrote:
             | Please use the newer Cookbook!
             | https://lispcookbook.github.io/cl-cookbook/data-
             | structures.h...
             | 
             | For a short hash-table notation, I now use Serapeum's dict:
             | (dict :a 1 :b 2)
             | 
             | and that's it, you now have a hash-table (with a
             | representation than can also be read back in by the lisp
             | reader).
        
         | kqr wrote:
         | > Lispers say that I could use specialized tooling to
         | compensate for poor ergonomics
         | 
         | Well, in input- and tooling constrained environments I have
         | preferred Lisp for its syntactic simplicity. I wouldn't call it
         | poor ergonomics.
        
           | whartung wrote:
           | I find coding in Lisp to be very comfortable. If I ever
           | decided to pull the trigger and swap [] for (), it would
           | remove a large portion of the "chording" that happens with
           | coding. And I by "chording" I refer to the shift key.
           | 
           | I love kebab-case-its-so-easy-to-type. May as well be spaces.
           | You can pretty much do everything with 26 lower case
           | characters, the -, space bar, and (). What's that, 30
           | characters? I have 46 unshifted keys I can use, plus the
           | spacebar. So thats 16 more keys for the more common symbols.
           | Just gotta remap the keyboard a bit.
           | 
           | Recall early Lisp actually used things like (plus ...) and
           | (minus ...).
           | 
           | Not have to chord is a real benefit, to me, when it comes to
           | typing. Lisp just flows that way. For me my-class I find a
           | lot easier than either MyClass, or my_class.
        
         | KingMob wrote:
         | > Lisp de-prioritizes legibility at a glance and concision in
         | order to give maximum priority to simplicity in
         | metaprogramming.
         | 
         | I'd need to see a source for this claim to believe that S-exprs
         | are primarily for the benefit of metaprogramming. They have a
         | lot of ancillary benefits in terms of uniformity, simplicity,
         | and scope. Plus, learning paredit gives a lot of power at the
         | keyboard, and you can't build an editing setup without
         | something like S-exprs (or angle bracket equivalents like
         | XML/HTML).
        
       | mindslight wrote:
       | I've got this theory that what people actually don't like about
       | Lisp parentheses is not the amount of nesting, nor the opening
       | paren's prefix location, but rather that parentheses always
       | include a hard nesting apply (or really, the syntax layer
       | equivalent of a hard nesting list). In other words - most
       | programmers are used to having an assertion that ((foo)) (foo)
       | and foo are all equivalent, and also assume the basic top-level
       | production is _already some kind of list_ (not a single
       | expression). Lisp breaks these long-loved assumptions.
       | 
       | Writing Haskell feels quite Lispy in that the basic syntax is
       | quite generic, but you can nest parentheses all day long without
       | changing the meaning, or even use some generic operators ($, .)
       | to write out a structure in a clearer way. I'd love to see an
       | attempt at a Lisp syntax that felt similarly. (And yes, I know
       | there have been 2^37 attempts at different Lisp syntaxes, but I
       | still haven't found one in line with this idea)
        
       | Beijinger wrote:
       | This is why every sane person uses an RPN calculator. :-0
        
       | norir wrote:
       | This comes off more as a series of rationalizations than a
       | compelling argument. Lisp is easy for a computer to parse, not
       | great for humans. There other languages with comparably rich
       | metaprogramming that syntactically have a better tradeoff between
       | computer and human legibility.
        
         | worewood wrote:
         | Yeah, it reads like "parentheses are good, here is a 50-page
         | essay on why" - sounds like the author is grasping at straws.
        
         | phoe-krk wrote:
         | _> Lisp is easy for a computer to parse, not great for humans._
         | 
         | Which is why "best of both worlds" approaches work best in my
         | opinion, no matter if it's "just" automatic indentation and
         | paren-counting that leaves actual editing to humans, or more
         | intrusive approaches like Parinfer (mentioned in the article)
         | that automatically infer parenthesisation from code indentation
         | and edit the code as appropriate.
         | 
         | Properly indented Lisp is easy to read (for me), and badly
         | indented code can be misread even in languages like C++.
        
         | packetlost wrote:
         | Nah, parenthesis are easy to parse with the eye if you spend
         | even a small amount of time working with them. You still have
         | to indent reasonably like you would with every other syntax.
        
         | gary_0 wrote:
         | If I wanted to I could get used to reading Lisp. I could also
         | get used to reading Klingon. But I never had to get used to
         | reading Python.
        
         | davexunit wrote:
         | Speak for yourself, I find the lisp syntax to be among the
         | easiest to read.
        
           | norir wrote:
           | I am not a lisp hater. There are advantages to its syntax
           | relative to other languages. I am just saying that in the
           | space of programming languages, there exist other languages
           | capable of solving the same problems as lisp in essentially
           | the same way using fewer characters simply by removing some
           | of the excessive parens and adding syntactic support for the
           | most common operations (assignment, scoping, calling
           | functions, data structure instantiation). Even if the total
           | characters is a wash, not having to chase down dangling
           | missed closed brackets or rely on an external tool to
           | highlight 15 levels of parens and/or auto insert 10 ugly
           | parens at thee end of a nested code block is a nice QOL
           | improvement in my opinion.
           | 
           | Again, none of this is saying that lisp is bad. I just think
           | we can have nicer things and preserve the important semantic
           | insights that lisp gives us.
        
       | camgunz wrote:
       | Parens are powerful in lisp(s) because it means the syntax is its
       | own AST and can be manipulated by itself (macros). It's one of
       | those exploding head moments that you (including me) can get
       | totally ensorceled by. But--as with all language features--I
       | think pragmatism demands that we ask "what incredible software
       | has this produced", and I can't think of any standouts.
        
         | Turing_Machine wrote:
         | This site is written in Lisp, I believe (a dialect called Arc).
         | 
         | Not only that, but the site and Y Combinator as a whole likely
         | wouldn't exist without Lisp.
         | 
         | https://paulgraham.com/avg.html
         | 
         | (Wow! Over 20 years ago!)
        
           | iainmerrick wrote:
           | Has anything else notable been done with Arc?
           | 
           | Rather than "Lisp gives you superpowers", maybe the story is
           | actually "there were some very effective developers who
           | happened to use Lisp to build their stuff".
        
           | camgunz wrote:
           | Yeah but I'm skeptical that there's anything about HN's
           | software that's uniquely enabled by being written in Arc. I
           | want someone to point to a feature in a Lisp and say, "you
           | can write X software only in Z because there's Y feature".
           | I'll even accept "significantly more easily" instead of
           | "only" here. For example: "you can write secure kernels only
           | in Rust because it has direct hardware access and memory
           | safety" (let's stipulate this is all true).
        
         | tmtvl wrote:
         | There are various problems with 'what incredible software has
         | this produced?'.
         | 
         | Various incredible software which has been produced date back
         | from before the Lisp machine went the way of the dodo. An
         | example of this would be Genera.
         | 
         | Other incredible software has a very specific type of draw
         | which doesn't work for everyone. Examples would be Guix and
         | Emacs.
         | 
         | And yet other software is very niche, leading to it being
         | barely discoverable. As Lisp isn't a hot programming language
         | (like Rust is), these projects don't usually get 'this thing
         | was programmed in Lisp' coverage. Things like NASA's SPIKE, for
         | example (cfr. <https://allegrograph.com/press_room/franzs-
         | allegro-cl-used-f...>).
        
           | User23 wrote:
           | Also the heavyweights of CAD, which have definitely affected
           | your life even if you're not consciously aware, are Lisp
           | based.
        
           | camgunz wrote:
           | Sure, mainly I mean like, "Lisp's feature X enabled us to do
           | thing Y that we couldn't do in other languages which let us
           | make baller software Z". I think other languages arguably
           | have this stuff: C/C++/Rust have direct hardware access and
           | speed, JavaScript has the browser, etc.
           | 
           | It's a little hard for me to pick out exactly what makes
           | Allegro CL [0] good at the AI thing. They do say you can also
           | write rules in Prolog, which makes me think the advantage
           | isn't down to Lisp but rather the library code they wrote on
           | top of it.
           | 
           | > Other incredible software has a very specific type of draw
           | which doesn't work for everyone. Examples would be Guix and
           | Emacs.
           | 
           | Yeah, maybe this is one. I almost listed Emacs, but I'm a
           | little torn (also a Vim user so don't trust me here haha).
           | Lisp is inextricable from Emacs, but I'm not familiar enough
           | to say whether or not you would have an appreciably harder
           | time writing an equivalent app in, say, Lua. Maybe! Maybe
           | live reload is core to the experience, etc. Like I said I'm
           | not super familiar.
           | 
           | So yeah, I should clarify that I don't think the software
           | needs to take over the world to meet my criteria here. I
           | think niche software can totally qualify; in fact I think
           | it's more likely to qualify. Basically it's "we wrote a
           | unique, useful program in Lisp that we really couldn't have
           | written in a different language." Does Emacs fit the bill
           | here? Maybe, but I kind of doubt it.
           | 
           | [0]: https://franz.com/products/allegrocl/
        
       | 1letterunixname wrote:
       | And if they had just used an RPN CAS like Erable... ;]
       | 10 [ENTER] foo [STO]              << foo 32 + >> bar [STO]
       | foo bar *
        
       | kleiba wrote:
       | > _This code does exactly the same thing as Rust code_
       | 
       | Except it doesn't - because after the final parenthesis of the
       | Lisp snippet, the let-binding is finished, i.e., the introduced
       | variables are no longer in scope. That is not the case after the
       | end of the last line in the Rust example.
       | 
       | The whole point of parentheses is to mark where something starts
       | and where it ends. Here, it is where the scope of the let-defined
       | variables starts and ends. Without some sort of "parenthesizing",
       | there might be a start, but it's much harder to mark where
       | something ends.
        
         | sgeisenh wrote:
         | I think it's more subtle than that. Rust has a notion of non-
         | lexical lifetimes (https://rust-
         | lang.github.io/rfcs/2094-nll.html) and the compiler often
         | completely avoids the use of the stack for small, trivially
         | droppable values, regardless of their lexical scope. In some
         | ways, `let` in Rust is a more C-like variant of the `let ... in
         | ... end` construct from OCaml.
         | 
         | Not to mention that you can always introduce a new lexical
         | scope with `{ ... }` in Rust code.
        
           | User23 wrote:
           | I believe there are Common Lisp implementations that will
           | happily stack allocate dynamic extent values.
        
       | tmtvl wrote:
       | I like Lisp syntax because the following works:
       | (if (<= lower-bound position upper-bound)           ...)
       | 
       | And the following doesn't:                 if (lower_bound <=
       | position <= upper_bound)         ...
        
         | User23 wrote:
         | Also                 (+)
         | 
         | And                 (*)
         | 
         | Are well defined.
        
       | nyrikki wrote:
       | Mostly this article is about people being exposed to
       | M-expressions in elementary math.
       | 
       | As someone whos first programming post BASIC was on a Symbolics
       | machine, coding _conventions_ worked just fine with s-expressions
       | 
       | The Polish notation is probably what most people are reacting to
       | unless you used the reverse version with an HP calculator.
       | 
       | Whitespace isn't significant in C like the author suggests, it is
       | coding convention.
       | 
       | COBOL is a counterexample for whitespace significance not helping
       | with readability.
       | 
       | But for lisp the parentheses was a non-issue with just a few
       | emacs macros.
       | 
       | But that is the same with most modern languages outside of
       | novelties like bf
        
       | ur-whale wrote:
       | Very long article to find a justification for a Stockholm
       | syndrome.
        
       | bfung wrote:
       | Lisp code is == to its AST, the programmer is literally writing
       | the AST that runs, hence meta programming is really easy.
       | 
       | The trade-off ends up being that metaprogramming becomes harder
       | while syntax gets changed
       | 
       |  _shrug_ , do it long enough and the parans disappear:
       | Q: How can you tell when you've reached Lisp Enlightenment?
       | A: The parentheses disappear.         ~ Anonymous
        
       | chalcolithic wrote:
       | Yet another chance to regret that REBOL has never caught on
        
       | anthk wrote:
       | As I use vim/slimv, I don't care, paredit fixes that.
        
       | wduquette wrote:
       | Do we though? Do we really need to talk about parentheses?
       | 
       | I've learned a lot from LISP and its variants over the years, and
       | I understand the value of LISP syntax for meta-programming, but
       | if any of this was truly compelling we'd have all been doing it
       | for years.
       | 
       | Every language has its own form of Stockholm Syndrome, I guess.
        
       | valcron1000 wrote:
       | Except for the structural editing enabled by the usage of
       | parenthesis, all the other features can be achieved without them
       | in ex. Haskell:                 a =        let foo = 10
       | bar = foo + 32        in  foo * bar            b =        1
       | & (+ 2)        & (* 9)        & (/ 5)
        
       ___________________________________________________________________
       (page generated 2024-02-12 23:01 UTC)