A linguagem Python homoicônica
(aljamal.substack.com)Implementando o "Lisp in Lisp" de McCarthy em Python
-
O Lisp, desenvolvido por John McCarthy no início da década de 1960, possui a característica de homoiconicidade, em que código e dados podem ser intercambiáveis
- No Lisp, a fronteira entre código e dados fica difusa
- Com isso, o Lisp consegue representar a si mesmo de forma natural
-
Meio página de código no rodapé da página 13 do manual do Lisp 1.5 era o próprio Lisp
- Alan Kay chamou isso de "as equações de Maxwell do software"
- Todo o universo da programação está comprimido em apenas algumas linhas de código
-
Para entender isso, uma boa forma é reimplementar em Python o código de "Lisp in Lisp"
- A maioria dos programadores não está acostumada com a sintaxe do Lisp, mas provavelmente está com a do Python
- O objetivo é reescrever em Python preservando ao máximo o espírito do código Lisp
M-expression e S-expression do Lisp
-
Originalmente, o Lisp tinha dois sabores de sintaxe
- M-expression (metaexpressão): o sabor de código
- S-expression (expressão simbólica): o sabor de dados
- As duas são semanticamente equivalentes
-
O código de "Lisp in Lisp" foi escrito em M-expression e implementa Lisp com S-expression
-
Uma forma de fazer isso é converter a estrutura de M-expression do Lisp em estrutura de código Python, e representar S-expression como listas Python
- Lisp significa List Processing e usa apenas uma única estrutura de dados: listas
Primeira implementação
-
As operações básicas do Lisp foram implementadas com funções básicas de listas do Python
- atom(x): verifica se x é uma lista
- eq(x,y): verifica se x e y são iguais
- car(x): retorna o primeiro elemento da lista
- cdr(x): retorna o restante da lista
- cons(x,y): adiciona um átomo à lista
- append(x,y): concatena duas listas
-
Ignorando alguns elementos básicos recursivos, é possível implementar rapidamente um interpretador para um subconjunto do código "Lisp in Lisp" com ajuda do Llama3-70b
Segunda implementação
-
A primeira implementação não tinha suporte a lambda
- Lambda é a principal forma de definir e chamar funções no Lisp
- Sem lambda não dá para implementar recursão, e sem recursão não há completude de Turing
-
Para implementar lambda, são necessários os elementos básicos assoc(x,y) e pairlis(x,y)
- assoc(x,y) é uma busca de chave/valor em dicionário usando lista de associação
- pairlis(x,y) é equivalente a
zip(x,y)em Python
-
Como o Lisp não tinha loop, até uma busca linear simples precisava usar recursão
- Mas em Python isso pode ser convertido elegantemente com list comprehensions
- O mesmo vale para evcon e evlis, que também podem ser convertidos em loops
-
A função eval originalmente recebe dois argumentos no Lisp
- O primeiro é a expressão (s-exp), o segundo é o ambiente (environment), uma lista de chave/valor
- O ambiente mantém os vínculos de variáveis de LAMBDA
- Usa a técnica de escopo dinâmico (dynamic scoping)
Opinião do GN⁺
-
É uma tentativa interessante de imitar em Python a característica de homoiconicidade do Lisp. Ainda assim, parece haver limites para transportar perfeitamente as características exclusivas do Lisp. Para experimentar o charme do Lisp, o melhor continua sendo aprender o próprio Lisp.
-
É impressionante ter implementado em Python recursos poderosos do Lisp, como funções lambda e escopo dinâmico. Mas parece haver muitas limitações para aplicar isso em projetos reais. Para fins educacionais ou de pesquisa, parece ter valor.
-
Esse tipo de tentativa pode servir como oportunidade para refletir profundamente sobre a essência das linguagens de programação. Independentemente da sintaxe e da implementação da linguagem, isso permite adotar uma visão a partir do paradigma de programação.
-
Aprender linguagens da família Lisp pode ajudar a obter insights sobre programação funcional e metaprogramação. Também vale a pena olhar para linguagens Lisp modernas como Scheme, Clojure e Racket.
1 comentários
Uma variação de Lisp incorporada ao Python
https://hylang.org/