Parentheses are more annoying in infix

There's a lot of code in functional languages written with a C or Java accent. The reverse is much rarer, but I have seen some: C++ written with a Lisp accent.

I didn't like it.

I didn't like the fooP convention for predicates. I didn't like the large multi-line expressions. And I especially didn't like the redundant parentheses.

What? A lisper doesn't like parentheses?

Parens are not high on the list of things that bother me in Lisp. They're only a little verbose, only a little distracting, only a little trouble to match. Large expressions don't bother me either; they're clearer than the alternative. And I like foo-p, because it's short and pronounceable.

Was I just objecting to C++ that didn't look like C++? Was I offended by contact between pretty Lisp and icky C++?

For fooP, that's probably the whole of it. It's camelCase instead of hyphenated, so it looks wrong as Lisp, and it's not standard C++ style, so it looks wrong as C++. And I'd rather not have to explain to other C++ programmers why I'm using a convention from some weird academic language. But I don't have a substantive objection.

For the other two features, I do.

Large expressions in prefix notation are easy to parse. The root operator is plainly visible at the beginning, and indentation goes a long way toward making the structure clear. Large expressions in infix are not so easy. The root operator is buried somewhere in the middle, and one must parse much of the expression to find it. There's no easy way to indent infix expressions, so breaking an expression across multiple lines doesn't alleviate much of the parsing load. This is why programmers in infix languages usually prefer to break such expressions into multiple statements.

Parentheses in Lisp are consistent: they all delimit lists, and almost all delimit forms. The semantics of the forms may be arbitrarily variable, but those of the parens are always the same. In C++, however, parentheses have several different meanings. They sometimes override precedence, sometimes call (or declare) functions, sometimes do typecasts, and sometimes delimit conditions in control structures. So a nest of parentheses in C++ is much more ambiguous than in Lisp, and it takes more parsing effort to determine which ones are which.

This goes some way toward explaining why so many programmers are suspicious of Lisp's syntax. Large expressions and nests of parentheses are suspicious in infix languages, and this suspicion does not instantly vanish in a new language.

3 comments:

  1. Some good insights here, thanks!

    I would add as an aside, though, that even in Lisp parens do have multiple meanings.

    In some dialects of Lisp, for example, like Common Lisp, parens introducing the car of the form (as in an explicit lambda combination) are slightly different than evaluation parens, even if syntactically compatible. Scheme takes another position on this, but it's necessary for it to be a Lisp1 as a consequence of that. A Lisp2 necessarily does a different kind of evaluation here.

    Another example is the Muddle (MDL) programming language that Zork was written in long ago, which tried to tease out some of those meanings. There is a code snippet here that illustrates the difference, as some Lispy parens are replaced by angle brackets and others remain parens: http://en.wikipedia.org/wiki/MDL_%28programming_language%29

    So it might not be that Lisp is so uniform as that it's slightly different. Sometimes even the fear of difference (which I think is your point) is enough, though, and it almost doesn't matter if it is actually different. The fact of the difference is an easy justification of the fear, but fear can be justified even without proof. It's OK to fear crossing a busy street, and getting hit is certainly a proof that there was warranted fear, but we shouldn't be too quick to dismiss the fear in those who manage not to get fit. Their fear was still justified. A tricky business, fear.

    I also took on the parentheses issue in my Slashdot interview a dozen+ years ago. People wanting further thoughts on that might go there: http://developers.slashdot.org/story/01/11/03/1726251/kent-m-pitman-answers-on-lisp-and-much-more

    ReplyDelete
    Replies
    1. I don't find the ((lambda ...) ...) parentheses confusing, because they do essentially the same thing in a different namespace. (I might find ((setf foo) ...) confusing, but who writes that?) On the other hand, I do sometimes find the non-form parentheses in (let ((foo bar) ...) ...) confusing, especially when I'm reading from the inside out and haven't noticed the LET yet. I'm not sure whether writing them with different brackets is an improvement, since it makes paren-matching harder.

      I think most of the trouble in C-like languages comes from the grouping and function-call senses, which are common and intermixed. If so, redundant parentheses would be less confusing in languages without function-call parentheses, like Haskell. (Although Haskell has no other common parentheses either.)

      Delete
  2. > There's no easy way to indent infix expressions, so breaking an expression across multiple lines doesn't alleviate much of the parsing load.

    Of course there is. It looks like a sideways tree -- which is precisely what makes the large prefix expression readable.

    ReplyDelete

It's OK to comment on old posts.