On Scheme

Thoughts on Scheme and teaching Scheme

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.

About these ads

23 Responses to “Lisp Without Parentheses”

  1. …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.

    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

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

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

  4. Marc said

    Maybe if the parens are truly irritating to you, you might enjoy Dylan?

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

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

  7. Peter said

    Sorry I do Scheme, not CL, and Scheme doesn’t support this kind of reader macro.

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

  9. * 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?

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

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

  12. 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:

  13. Bill Birch said

    Link:

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

  15. Bill Birch said

  16. Bill Birch said

    http://www.lispin.org/

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

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

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

  20. Alpheus said

    I don’t think getting rid of parentheses will make Lisp nicer for newbies, but I’m interested in parentheses-less Lisp for a different reason: I’d like to use Lisp as a command line shell by gradually building up functions for that purpose; however, I don’t want to write things like

    (ls :lha “*.txt”)

    every time I wanted to read a directory!

  21. […] hacerlo en Python pero, para una vez que tengo oportunidad de aprovechar el tiempo que invertí jugando con los paréntesis de Lisp en la facultad… […]

  22. Howlsedhes Services said

    Take the parens out of LISP and you have Forth backwards. Scarcely a paren in sight. For operators with an indefinite number of arguments you would need a terminating symbol, like “mark” in Postscript (IIRC). But then in LISP everything is basically data, whereas in Forth everything is basically an operator/function (ignoring a few special item in each language).

  23. Adriano said

    I’m working on a way to write Lisp using Gingko, our tree-based text editor:

    http://blog.gingkoapp.com/features/gingko-as-a-lisp-editor

    Since our documents are trees already, all we need to do is wrap each “card” in parentheses (recursively) on export, and we have a tree to lisp format.

    If anyone stumbles on this, let me know what you think!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: