Sobre Lisp e seus parênteses

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?

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *