Can a language have Lisp's powerful macros without the parentheses?

Sure, the question is whether the macro is convenient to use and how powerful they are.

Let's first look how Lisp is slightly different.

Lisp syntax is based on data, not text

Lisp has a two-stage syntax.

A) first there is the data syntax for s-expressions

examples:

(mary called tim to tell him the price of the book)

(sin ( x ) + cos ( x ))

s-expressions are atoms, lists of atoms or lists.

B) second there is the Lisp language syntax on top of s-expressions. Not every s-expression is a valid Lisp program.

(3 + 4)

is not a valid Lisp program, because Lisp uses prefix notation.

(+ 3 4)

is a valid Lisp program. The first element is a function - here the function +.

S-expressions are data

The interesting part is now that s-expressions can be read and then Lisp uses the normal data structures (numbers, symbols, lists, strings) to represent them.

Most other programming languages don't have a primitive representation for internalized source - other than strings.

Note that s-expressions here are not representing an AST (Abstract Syntax Tree). It's more like a hierarchical token tree coming out of a lexer phase. A lexer identifies the lexical elements.

The internalized source code now makes it easy to calculate with code, because the usual functions to manipulate lists can be applied.

Simple code manipulation with list functions

Let's look at the invalid Lisp code:

(3 + 4)

The program

(defun convert (code)
   (list (second code) (first code) (third code)))

(convert '(3 + 4)) -> (+ 3 4)

has converted an infix expression into the valid Lisp prefix expression. We can evaluate it then.

(eval (convert '(3 + 4))) -> 7

EVAL evaluates the converted source code. eval takes as input an s-expression, here a list (+ 3 4).

How to calculate with code?

Programming languages now have at least three choices to make source calculations possible:

  1. base the source code transformations on string transformations

  2. use a similar primitive data structure like Lisp. A more complex variant of this is a syntax based on XML. One could then transform XML expressions. There are other possible external formats combined with internalized data.

  3. use a real syntax description format and represent the source code internalized as a syntax tree using data structures that represent syntactic categories. -> use an AST.

For all these approaches you will find programming languages. Lisp is more or less in camp 2. The consequence: it is theoretically not really satisfying and makes it impossible to statically parse source code (if the code transformations are based on arbitrary Lisp functions). The Lisp community struggles with this for decades (see for example the myriad of approaches that the Scheme community has tried). Fortunately it is relatively easy to use, compared to some of the alternatives and quite powerful. Variant 1 is less elegant. Variant 3 leads to a lot complexity in simple AND complex transformations. It usually also means that the expression was already parsed with respect to a specific language grammar.

Another problem is HOW to transform the code. One approach would be based on transformation rules (like in some Scheme macro variants). Another approach would be a special transformation language (like a template language which can do arbitrary computations). The Lisp approach is to use Lisp itself. That makes it possible to write arbitrary transformations using the full Lisp language. In Lisp there is not a separate parsing stage, but at any time expressions can be read, transformed and evaluated - because these functions are available to the user.

Lisp is kind of a local maximum of simplicity for code transformations.

Other frontend syntax

Also note that the function read reads s-expressions to internal data. In Lisp one could either use a different reader for a different external syntax or reuse the Lisp built-in reader and reprogram it using the read macro mechanism - this mechanism makes it possible to extend or change the s-expression syntax. There are examples for both approaches to provide a different external syntax in Lisp.

For example there are Lisp variants which have a more conventional syntax, where code gets parsed into s-expressions.

Why is the s-expression-based syntax popular among Lisp programmers?

The current Lisp syntax is popular among Lisp programmers for two reasons:

1) the data is code is data idea makes it easy to write all kinds of code transformations based on the internalized data. There is also a relatively direct way from reading code, over manipulating code to printing code. The usual development tools can be used.

2) the text editor can be programmed in a straight forward way to manipulate s-expressions. That makes basic code and data transformations in the editor relatively easy.

Originally Lisp was thought to have a different, more conventional syntax. There were several attempts later to switch to other syntax variants - but for some reasons it either failed or spawned different languages.


Absolutely. It's just a couple orders of magnitude more complex, if you have to deal with a complex grammar. As Peter Norvig noted:

Python does have access to the abstract syntax tree of programs, but this is not for the faint of heart. On the plus side, the modules are easy to understand, and with five minutes and five lines of code I was able to get this:

>>> parse("2 + 2")

['eval_input', ['testlist', ['test', ['and_test', ['not_test', ['comparison', ['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['term', ['factor', ['power', ['atom', [2, '2']]]]], [14, '+'], ['term', ['factor', ['power', ['atom', [2, '2']]]]]]]]]]]]]]], [4, ''], [0, '']]

This was rather a disapointment to me. The Lisp parse of the equivalent expression is (+ 2 2). It seems that only a real expert would want to manipulate Python parse trees, whereas Lisp parse trees are simple for anyone to use. It is still possible to create something similar to macros in Python by concatenating strings, but it is not integrated with the rest of the language, and so in practice is not done.

Since I'm not a super-genius (or even a Peter Norvig), I'll stick with (+ 2 2).


Here's a shorter version of Rainer's answer:

In order to have lisp-style macros, you need a way of representing source-code in data structures. In most languages, the only "source code data structure" is a string, which doesn't have nearly enough structure to allow you to do real macros on. Some languages offer a real data structure, but it's too complex, like Python, so that writing real macros is stupidly complicated and not really worth it.

Lisp's lists and parentheses hit the sweet spot in the middle. Just enough structure to make it easy to handle, but not too much so you drown in complexity. As a bonus, when you nest lists you get a tree, which happens to be precisely the structure that programming languages naturally adopt (nearly all programming languages are first parsed into an "abstract syntax tree", or AST, before being actually interpreted/compiled).

Basically, programming Lisp is writing an AST directly, rather than writing some other language that then gets turned into an AST by the computer. You could possibly forgo the parens, but you'd just need some other way to group things into a list/tree. You probably wouldn't gain much from doing so.