29

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".

4
  • 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 8 '11 at 7: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. Aug 18 '11 at 18:20
  • 5
    because they can?
    – Muad'Dib
    Aug 19 '11 at 4: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 2 '13 at 11:09
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.

1
  • 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
15

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.

7
  • 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. Aug 7 '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. Aug 7 '11 at 16:31
  • 1
    @Mason Wheeler Yes, Exactly.
    – Chiron
    Aug 7 '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.
    – Steve314
    Aug 8 '11 at 7:15
  • 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 8 '11 at 8: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.

3
  • What an honor to get an answer from you :) Thanks.
    – Chiron
    Aug 18 '11 at 21:29
  • What editor moves by s-expressions? More specifically, how do I get vim to do that?
    – hasen
    Aug 19 '11 at 1:40
  • 2
    @HasenJ You might need the vimacs.vim plugin. :P
    – Mark C
    Aug 27 '11 at 20:25
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!

1
  • 2
    This is one of the funniest and most underrated answers I've seen on this site. I tip my hat.
    – byxor
    Apr 9 '17 at 2: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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.