Qualquer código em Lisp terá muitos, muitos parênteses, e eu nunca tinha lido uma explicação convincente sobre o porquê ou a vantagem disso. Até então eu aceitava que as coisas simplesmente são assim:
(let ((x 10))
(let ((y (+ x 10)))
(list x y)))
(define (fib n)
(cond ((= n 0) 0)
((= n 1) 1)
(else (+ (fib (- n 1))
(fib (- n 2))))))
(defun fatorial (x)
(if (zerop x)
1
(* x (fatorial (- x 1)))))
Há alguns dias atrás finalmente eu li uma explicação que fez tudo ficar mais claro para mim: Ron Garret, em seu artigo Why Lisp?, trata especificamente de explicar o motivo de tantos parênteses e esclarecer porque a quantidade de parênteses é justamente uma das coisas que torna Lisp tão poderosa.
A questão não é que a sintaxe de Lisp é diferente: a questão é que o modo de pensar a programação em Lisp é que é totalmente diferente! Em linguagens “comuns” pensamos que programas operam em dados e existe uma distinção clara entre o que é um programa e o que são os dados. Em Lisp, por outro lado, programas são dados, não existe uma distinção artificial entre os dois conceitos. E esse é o ponto fundamental.
Já que, em Lisp, programas são dados, deve haver uma maneira simples e única de representar os “dois” conceitos, de modo a nos permitir manipulá-los da mesma forma. Essa maneira foi a criação, por John McCarthy, das Expressões Simbólicas (S-Expressions) (para uma descrição formal, ver o Internet Draft “S-Expressions“, publicado pelo Internet Engineering Task Force, ou o trabalho original de McCarthy: “Recursive Functions of Symbolic Expressions and Their Computation by Machine“).
As S-Expressions são uma notação para representar dados hierárquicos em uma lista aninhada. Como em Lisp programas são dados, as S-Expressions também são usadas para serializar e de-serializar o próprio código fonte com extrema facilidade, pois são a representação mínima, necessária e suficiente para dados hierárquicos! Repetindo aqui o exemplo de Garret, compare o seguinte:
S-expression: (abc pqr xyz)
JSON: ['abc', 'pqr', 'xyz']
XML: <list>
<item>abc</item>
<item>pqr</item>
<item>xyz</item>
</list>
Outro exemplo de Garret:
S-expression: (for x in foo collect (f x))
JSON: ['for', 'x', 'in', 'foo', 'collect', ['f', 'x']]
Considerar que programas são dados e utilizar uma mesma representação para escrever códigos como dados foi a grande sacada da Lisp. Isso é uma característica extremamente poderosa da linguagem e permite que programas manipulem programas, bem como permite escrever compiladores e interpretados com grande facilidade.
A sintaxe da Lisp, formada únicamente por S-Expressions, é a sintaxe mais simples possível: para representar tudo (programas e dados), são necessários apenas três coisas:
- Átomo (“atom”, “token”): um dado, uma string, o nome de uma função
- Separador de átomos: espaço em branco
- Delimitador de blocos: que são os parênteses
As outras linguagens utilizam diversos tipos de pontuação (; ,) e diversos delimitadores de blocos: (), [], {}.
Lisp parece que usa muitos parênteses, mas isso é porque em Lisp só existem os parênteses! E são exatamente os parênteses (na verdade, as S-Expressions) que permitem que o conceito programas são dados seja implementado.
Para os detalhes, leia o artigo de Ron Garret: Why Lisp?