Lisp Without Parentheses
Posted by Peter on April 16, 2006
Well it’s impossible to remove all of the parentheses in Lisp, but I can think of several methods to eliminate the bulk of them. One alternative to parentheses is being sensitive to white space, in a way that is reminiscent of Python. The simple description is as follows: each line automatically has an opening parenthesis added before it, and a closing parenthesis added after it, except under the following circumstances: if the following line has one extra level of indentation omit the closing parenthesis. If the line in question is indented two or more levels beyond the previous line add an extra opening parenthesis for every new level of indentation beyond the first. If the line following the line in question is less indented than the line we are examining add an extra closing parenthesis for each level less. (the end of the file should be treated as a line with 0 indentation of course)
Let’s apply these rules to some simple cases:
A cond statement of the following nature:
(cond ((< x -1) (* x x)) ((> x 1) (* x x)) ((< x 0) (sqrt (abs x))) (else (sqrt x)))
would become:
cond (< x -1) (* x x) (> x 1) (* x x) (< x 0) (sqrt (abs x)) else (sqrt x)
if we are willing to take up some more lines it could also become:
cond (< x -1) * x x (> x 1) * x x (< x 0) sqrt (abs x) else sqrt x
Admittedly this expansion looks rather verbose for such a simple cond statement, but if the cond was more complex, and had more instructions in each branch, it would not be too much of an expansion, because it is likely the programmer would have already indented it in this fashion.
Now consider the let statement:
(let ((a (+ 1 v)) (b (car z)) (c (avg 8 3 w)) (d w)) (body-goes-here))
This statement could be written as:
let (a (+ 1 v)) (b (car z)) (c (avg 8 3 w)) (d w) body-goes-here
or as:
let a (+ 1 v) b (car z) c (avg 8 3 w) d w body-goes-here
So far everything looks pretty nice, but there are a few problems with this method. For example: consider the case where programmers break a function containing many, possibly complicated, arguments onto several lines:
(my-large-function (car (cdr y)) (lambda (x) (/ x 2)) my-list)
we want to break it up like so:
my-large-function car (cdr y) lambda (x) (/ x 2) my-list
but unfortunately the rules we have laid out dictate the my-list should be wrapped with parentheses, and thus will be called like a function. This can also be a problem when you want to put more than one function call on a single line, for example:
(lambda (x) (display x) (newline) (* x 42))
is not properly translated as:
lambda (x) (display x) (newline) * x 42
since the result of (display x) is not a function to be applied to the result of (newline).
There are several ways to overcome this, for example using values (in an ideal world, where it worked like you expected it to) and begin. However a simpler solution is to simply add a special prefix the language that when encountered before a line instructs the compiler/reader to add one less pair of parentheses around the line. I would suggest the \ character, since it has been used in other contexts as an escape character. Our examples then would be written (correctly) as follows:
my-large-function car (cdr y) lambda (x) (/ x 2) \ my-list lambda (x) \ (display x) (newline) * x 42
Note: \ is not notation for the continuation of a line.
Well, it’s something to think about at least. I know some people will object for the same reasons they do to Python’s white space sensitivity, but consider this before you dismiss it out of hand: one of the biggest barriers to the adoption of Lisp is that the sheer bulk of parentheses can be intimidating, and a method such as this would hide the majority of them, especially the large mass of closing parenthesis that seems to end every complicated function.
As you may know I plan on experimenting with my own Scheme variant soon, and this is one of the ideas I think I will try out in it (as a compiler option of course, not a mandate), unless I hear a good reason not to.
Greg Buchholz said
The Haskell language uses additional indentation as your “special prefix”. So an expression continues until you reach a line with less indentation. Also, instead of the “\” at the beginning of a line, you could place it at the end of the previous line, as a line continuation character like in C and Ruby.
And for my experimental language, I’m thinking of reducing parens by having fixed arity functions/Polish Notation. So your “cond” example might look like…
cond
< x -1 * x x
> x 1 * x x
< x 0 sqrt abs x
else sqrt x
Peter said
Perhaps I wasn’t clear enough, the \ is not to indicate that the line is a continuation of the previous line, it indicate that the line is not a single expression, it is either multiple expressions or values. I can’t use additional indentation either, since that is taken already for more nesting depth (see the let example).
The reason I picked indentation is that people naturally indent, so I might as well try to exploit their efforts.
On using Ploish Notation: you really do need to find a way to acceept variable length functions (perhaps using some parentheses or a comma to seperat them), because few programmers would give up the list or quasiqoute functions.
michaelw said
Some time ago, I experimented with colorbox, an Emacs hack to hide parentheses and use colors to highlight structure.
Lispers tend to think that indentation is for humans and parentheses are for the compiler, so I aimed at providing some more visual aids there.
Marc said
Maybe if the parens are truly irritating to you, you might enjoy Dylan?
Peter said
No, not really, I have already posted on why Dylan is not a solution I like. Also I like parenthesis in general, as they add clarity, I am just proposing a way to make Lisp less intimidating to those new to it. Also I am not in favor of replacing some ()s with {}s or []s.
Daniel said
If you know how to write reader macros, you could get a working prototype of your idea in a few minutes. Try it, try programming in it, let us know how it feels.
Peter said
Sorry I do Scheme, not CL, and Scheme doesn’t support this kind of reader macro.
lee said
lisp is tough because of syntax and semantics. parens are weird, and making new functions at the drop of a hat is weird. if people start out coding in ruby, where you can write as much semantically lisp code as you want interspersed with as much semantically C code as you want, then people don’t get panicky about making a function, when they’ve been using curlybraces and bars. for most work, in ruby there’s much less typing (the identifiers are shorter) and much more ease of switching between OO/functional/imperative/blubbish/etc paradigms.
Leo Comerford said
* I would substitute
,for\– it seems a little more idiomatic and less jarring. Thus:my-large-function
car (cdr y)
lambda (x) (/ x 2)
, my-list
lambda (x)
, (display x) (newline)
* x 42
Contra Greg Buchholz, I think that the marker should indeed be at the start of the line after the indentation – it’s too easy to overlook at the end of the previous line.
* It should be possible to indent using either spaces or tabs, but mixing space and tab indentation in a single file should be illegal. (At the very least, it should certainly be illegal to combine spaces and tabs in the indentation of a single line.)
* It should be illegal to bump the indentation by more than one level in a single line using only whitespace. Instead, each level of “extra” indentation should be indicated like this:
let
. a (+ 1 v)
. b (car z)
. c (avg 8 3 w)
. d w
body-goes-here
This makes it less easy to misread such lines. Also, you’ll probably want to gauge the number of spaces per indent by observing the first indented line, and what if it’s doubly indented?
Peter said
I like the idea of , instead of \
I can see why you might want . to act as an extra bump out in indentation, but since this is 2006 I think it is reasonable to assume that people’s editors can handle indentation properly.
Bill Birch said
Hi,
I implemented a filter to do precisely this syntax some time ago. see http://sourceforge.net/docman/display_doc.php?docid=26474&group_id=69034
. I like it so much I have renamed my interpreter “lispin” meaning Lisp INdented. My next project is to change the syntax to use space-based indentation instead of tab characters. Then it will be quite Pythonic. (Tabs are evil).
I would encourage schemers to consider the indented form. It is really easy to type in, and there is no hassle with paren matching.
Love your blog.
Bill
Bill Birch said
Lispin is now running on the web at . Right now the site is minimal, but you will be able to try the syntax interactively. Syntax:
# for comments
~ for line continuation. e.g.
* 23 10
*
~ 23
~ 10
Also check out previous thread on the subject:
Bill Birch said
Link:
Bill Birch said
I give up! WordPress hates me…
http://groups.google.com.au/group/comp.lang.lisp/browse_frm/thread/8362b76c37964dd8/558caef475838ee7?q=parentheses&rnum=1#558caef475838ee7
Bill Birch said
Bill Birch said
http://www.lispin.org/
Srikumar said
Your topic set me thinking and I think I have a reasonable alternative to lisp syntax that’s similar to your description. Read about it here. I’ve also posted source code for a translator I wrote to convert the indentation based syntax to lisp/scheme code. Though the translator is minimal and I can’t vouch for bugfree-ness, its usable for experiments at least.
Cheers,
-Kumar
Dave Bayer said
I found this site by googling, after considering a similar idea; I made one improvement:
I like Haskell’s $ operator, although | has a lighter look. Add the following rule: An unescaped | introduces an opening parenthesis that is automatically closed at the end of the line. For example,
(let ((limit 5))
becomes
let || limit 5
Aliplyclearia said
Think about it… Are you encouraging my jocular synthesis Sorry, for off top, i wanna tell one joke) What lies at the bottom of the ocean and twitches? A nervous wreck.