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
.
Lisp syntax is just that: syntax. It has nothing to do with semantics. In other words, the syntax (ideally) shouldn't affect how you write programs; if you feel like you have to "completely forget how you write regular programs", it sounds like you're having difficulty with the semantics, not with the syntax.
Practical benefits of Lisp syntax include:
consistency. Function/macro/special-form followed by its arguments. No operator precedence issues. Grouping is usually explicit.
easy to parse and unparse. This makes it easier to write source code tools, such as static analyzers and code browsers.
extensibility
- user-defined macros. Users can extend their Lisp system's syntax.
- system-defined syntax. New special forms can be added to a Lisp system without having to worry about how their syntax interferes with existing forms.
You also threw in a couple of questions about side effects and functional programming. Those aren't related to Lisp syntax and should really be asked in a separate question.
Best Answer
You can do that, and much more, using sweet-expressions. There is already a working implementation of sweet-expressions for Scheme, which I encourage you to study. The basic idea is that you replace the standard S-expression reader with a sweet-expression reader; everything else stays the same, and you retain homoiconicity and the ability to use macros.
The question is how to implement sweet-expressions in Clojure, since curly braces are already used as a notation for maps, sets, and metadata.