30

Why does the Lisp community prefer to accumulate all the parentheses at the end of the function:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)))

Why not employ a convention like C or Java?
Well ok, Lisp is much more older than those languages, but I'm talking about the contemporary Lispers.

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

Note: Code snippet is from the book "The Joy of Clojure".

Chiron
  • 4,543
  • 3
  • 28
  • 37
  • 14
    I'm told that back in the days of punch cards, it was common practice to end a Lisp program with an extra card with 80 right parentheses on it, just to make sure there were enough to match all of the open parentheses in the program. – Alger Aug 08 '11 at 07:05
  • 2
    Wow, I like it. I've always hated the parenthesis, but your second example looks pleasant to me. Not as nice as Python, but improvement. – Eric Wilson Aug 18 '11 at 18:20
  • 5
    because they can? – Muad'Dib Aug 19 '11 at 04:00
  • It would be nice if a dialect of Clojure promoted indentation to being code structure. That would do away with the parenthesis altogether (like F#, etc..). Of course, parenthesis would still be legal, if desired. – intrepidis Jun 02 '13 at 11:09

5 Answers5

31

One reason Algol-based languages encourage the braces on their own line is to encourage adding more lines in between the delimiting braces without having to move the braces. That is, if one starts out with

if (pred)
{
  printf("yes");
}

it's easy to come along and add another statement within the braces:

if (pred)
{
  printf("yes");
  ++yes_votes;
}

Had the original form been

if (pred)
{ printf("yes"); }

then we'd have to have "moved" two braces, but my example is more concerned with the latter. Here, the braces are delimiting what's intended to be a sequence of statements, mostly invoked for side effect.

Conversely, Lisp lacks statements; every form is expression, yielding some value—even if in some rare cases (thinking of Common Lisp), that value is deliberately chosen to be "no values" via an empty (values) form. It's less common to find sequences of expressions, as opposed to nested expressions. The desire to "open up a sequence of steps until the closing delimiter" doesn't arise as often, because as statements go away and return values become more common currency, it's more rare to ignore the return value of an expression, and hence more rare to evaluate a sequence of expressions for side effect alone.

In Common Lisp, the progn form is an exception (as are its siblings):

(progn
  (exp-ignored-return-1)
  (exp-ignored-return-2)
  (exp-taken-return))

Here, progn evaluates the three expressions in order, but discards the return values of the first two. You could imagine writing that last closing parenthesis on its own line, but note again that since the last form is special here (not in the Common Lisp sense of being special, though), with distinct treatment, it's more likely that one would add new expressions in the middle of the sequence, rather than just "adding another one on to the end," as callers would then be impacted not just by any new side effects but rather by a likely change in return value.

Making a gross simplification, the parentheses in most parts of a Lisp program are delimiting arguments passed to functions—just like in C-like languages—and not delimiting statement blocks. For the same reasons we tend to keep the parentheses bounding a function call in C close around the arguments, so too do we do the same in Lisp, with less motivation to deviate from that close grouping.

The closing of the parentheses is of far less import than the indentation of the form where they open. In time, one learns to ignore the parentheses and write and read by shape—much like Python programmers do. However, don't let that analogy lead you to think that removing the parentheses entirely would be worthwhile. No, that's a debate best saved for comp.lang.lisp.

seh
  • 522
  • 3
  • 5
  • 2
    i think that adding at the end is not that rare, e.g. `(let ((var1 expr1) more-bindings-to-be-added) ...)`, or `(list element1 more-elements-to-be-added)` – Alexey Oct 31 '13 at 19:51
16

Because it does not help. We use indentation to show code structure. If we want to separate blocks of code, we use really empty lines.

Since Lisp syntax is so consistent, the parentheses are the definitive guide to indentation for both the programmer and the editor.

(For me, the question is rather why C and Java programmers like to throw around their braces.)

Just to demonstrate, assuming that these operators were available in a C-like language:

Foo defer_expensive (Thunk cheap, Thunk expensive) {
    if (Foo good_enough = force (cheap)) {
        return good_enough; }
    else {
        return force (expensive); }}

Two closing braces close two nesting levels. The syntax is obviously correct. In analogy to Python syntax, the braces are just explicit INDENT and DEDENT tokens.

Of course, this might not be the "one true brace style"TM, but I believe that is just historic accident and habit.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Svante
  • 448
  • 2
  • 10
  • 2
    Also, almost all lisp editors mark the matching parenthesis when closing (some also correct `)` to `]` or vice versa), so you know you closed the right amount even if you don't check the indentation levels. – configurator Aug 07 '11 at 16:25
  • 6
    WRT "the question", because "throwing around" closing tokens in the style of the second example lets you easily line them up with your eyes and see what closes what, even if you're just in a text editor with no automatic matching/highlighting features. – Mason Wheeler Aug 07 '11 at 16:31
  • @Mason Wheeler: I have actually experimented with Lisp-like formatting in C-like languages and I had no problem visually checking `}}}` against three levels of indentation. – Svante Aug 07 '11 at 16:42
  • 1
    @Mason Wheeler Yes, Exactly. – Chiron Aug 07 '11 at 21:07
  • 3
    TBH, what this says to me is that the parens in LISP are mostly redundant, with the possibility (as with C++) that the indentation may mislead - if indentation tells you what you need to know it should tell the compiler the same thing too, as in Haskell and Python. BTW - having the braces at the same indent level as the if/switch/while/whatever in C++ helps prevent cases where the indentation is misleading - a visual scan down the LHS tells you that every open-brace is matched to a close-brace and that indentation is consistent with those braces. –  Aug 08 '11 at 07:15
  • -1 because you're simply making assertions. Exactly the same statements could be made about Java and C (same for @configuratior's point) and would be true. So it does not explain the difference in conventions - seh has a much better point that is actually based about a difference in the languages. – Michael Borgwardt Aug 08 '11 at 08:50
  • 1
    @Steve314: No, the indentation is redundant. Indentation can mislead in Python and Haskell, too (think about one-off indentation or tabulars), it is just that the parser is mislead as well. I think that the parentheses are a tool for the writer and the parser, while indentation is a tool for the writer and the (human) reader. In other words, indentation is a comment, parentheses are syntax. – Svante Aug 08 '11 at 08:55
11

The code is much more compact then. Movement in the editor is by s-expressions anyway, so you don't need that space for editing. Code is read mostly by structure and sentences - not by following delimiters.

Rainer Joswig
  • 2,190
  • 11
  • 17
6

Lispers, you know, hatefully bletcherous as it may be, there is a certain je ne sais quoi to the second example:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

At first I couldn't put a finger on the source of the allure, so to speak, and then I realized it is just missing a trigger:

(defn defer-expensive [cheap expensive]      
  (if-let [good-enough (force cheap)]
    good-enough   ;   )
    (force expensive) 
  )
)

Voilà!

I'm going to practice my Lisp gangster grip now!

Kaz
  • 3,572
  • 1
  • 19
  • 30
  • 2
    This is one of the funniest and most underrated answers I've seen on this site. I tip my hat. – byxor Apr 09 '17 at 02:30
0

In my case I find the lines dedicated to delimiters a waste of screen space, and when you write code in C there is also the style of

if (pred) {
   printf("yes");
   ++yes_votes;
}

Why do people put the opening brace in the same line of the "if" to save space, and because it looks redundant to have its own line when the "if" already has its own line.

You reach the same result by putting together the parenthesis at the end. In C would look weird because statements end in semicolon and the pair of brackets doesn't open like this

{if (pred)
    printf("yes");
}

it is like that closing brace at the end, looks out of place. it opens like this

if (pred) {
    printf("yes");
}

giving you a clear view of the block and its limits by '{' & '}'

And with help of an editor that matches parenthesis by highlighting them like vim does, you can go to the end where all the parenthesis are packed and move through them easily and match all the opening parens and see the nested lisp form

(defn defer-expensive [cheap expensive]
    _(if-let [good-enough (force cheap)]
    good-enough
    (force expensive)_))

you can put your cursor in the closing paren in the middle and have the opening paren of the if-let highlighted.

kisai
  • 171
  • 1
  • 6