1 pontos por GN⁺ 3 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • O ponto central de Lisp não é a sintaxe com parênteses, mas a mentalidade de estender a linguagem para adequá-la ao problema; seu real valor aparece quando se aprende também REPL, pacotes, símbolos, condições e reinicializações
  • Graças à homoiconicidade, em que código e dados são ambos representados como listas, macros podem tratar código antes da avaliação como se fosse dado, criando novas estruturas de controle e abstrações sintáticas
  • Mesmo o while, ausente em Common Lisp, pode ser adicionado com defmacro, mas se for imitado com uma função, os argumentos são avaliados imediatamente e o body vira um valor, não código
  • O desenvolvimento em Lisp é mais próximo de um desenvolvimento guiado por REPL, em que o código é avaliado continuamente em um processo em execução, permitindo redefinir funções, macros e variáveis e evoluir o programa sem interrompê-lo
  • Quando a extensibilidade da linguagem se combina a um sistema vivo, é possível criar software extensível que expõe DSLs internas aos usuários; AutoLISP e Emacs são exemplos representativos

Lisp exige uma sensibilidade de programação diferente

  • Programadores que entram em contato com Lisp pela primeira vez estranham a sintaxe cheia de parênteses, a indentação peculiar e a forma como format recebe a saída padrão como primeiro argumento
  • Para aprender Lisp, é preciso ir além da leitura de código e lidar também com pacotes e símbolos, criação de projetos, importação de bibliotecas, REPL, condições e reinicializações
  • A mudança maior aparece na forma de compor algoritmos
    • Lisp torna possíveis coisas difíceis de fazer em outras linguagens e também muda o formato dos algoritmos
    • O fluxo passa a ser primeiro fazer a linguagem crescer de acordo com o problema e, depois, escrever o programa nessa linguagem
  • O paradoxo Blub é um conceito que explica por que programadores que só usaram linguagens menos poderosas têm dificuldade para perceber os pontos fortes de Lisp

Linguagem extensível

  • Lisp pode estender a si mesma, e especialistas em Lisp a chamam de programmable programming language
  • A ferramenta central é macro
    • Não se limita a eliminar código repetitivo, como macros em C, Rust e Swift
    • Macros de Lisp podem criar novos componentes que se tornam parte da linguagem
  • Common Lisp não tem um operador while padrão, mas é possível adicioná-lo diretamente com defmacro
(defmacro while (condition &body body)
  `(loop while ,condition do
     (progn ,@body)))
  • Essa macro while executa várias instruções de body enquanto condition for verdadeira
    • loop é uma macro para escrever iterações complexas
    • progn executa várias expressões em sequência e retorna o resultado da última expressão
  • Como resultado, é possível usar a forma (while ...) em vez de (loop while ... do (progn ...)), deixando o código mais curto e com uma estrutura parecida com o while de C

A diferença decisiva entre macros e funções

  • Se tentarmos criar o mesmo comportamento como uma função fake-while, ela falha
(defun fake-while (condition body)
  (loop while condition do
    (funcall body)))
  • Na chamada de fake-while, os argumentos são avaliados primeiro
    • condition se torna o valor verdadeiro t
    • (print counter) dentro de progn é executado e (decf counter) retorna 2
    • body se torna o valor 2, não um bloco de código
  • Depois disso, funcall espera uma função, mas recebe 2, então ocorre um erro de tipo como The value 2 is not of type FUNCTION
  • Já a macro while não avalia condition e body imediatamente; ela os preserva
    • É possível verificar o resultado da expansão com macroexpand-1
    • O compilador recebe o código na forma expandida (LOOP WHILE ... DO (PROGN ...))
  • Ao contrário de funções, macros não avaliam seus argumentos antecipadamente e os tratam como dados puros, o que torna essa transformação possível

Listas, s-expression e homoiconicidade

  • Programas em Lisp são compostos por uma sequência de symbolic expressions, ou s-expressions
  • Uma s-expression se divide em dois tipos
    • Átomo (atom): unidade de dados como número, string ou símbolo
    • Lista (list): coleção que contém átomos ou outras listas
  • O nome Lisp vem de LISt Processing, e as instruções que compõem um programa também são representadas como listas
  • Em Lisp, listas são tanto a principal estrutura de dados quanto a forma de escrever código
    • A propriedade de código e dados serem ambos representados como listas é chamada de homoiconicidade (homoiconicity)
  • A mesma lista (+ 1 2) se comporta de formas diferentes dependendo de ser avaliada ou não
    • (+ 1 2) é avaliada como código e retorna 3
    • '(+ 1 2) não é avaliada e retorna a lista de dados como está
  • Lisp usa notação polonesa, colocando o nome da função como primeiro elemento da lista e os argumentos depois
  • Como a fronteira entre código e dados é fina, macros podem transformar código-fonte e criar programas que escrevem programas

Abstração de código criada por macros

  • Ao estender a linguagem, é possível criar novos componentes mais expressivos para uma área específica do programa
  • Boilerplate repetitivo pode ser substituído por macros customizadas, reduzindo código e economizando tempo
  • É possível criar novas estruturas de controle para controlar o acesso a recursos e a avaliação de forms
  • Macros geram código, melhoram desempenho e oferecem abstração sintática que esconde código complexo e propenso a erros

Lisp é um sistema vivo

  • Lisp é tratado menos como uma simples linguagem de programação e mais como um sistema vivo em execução
  • Em linguagens comuns, o fluxo geralmente é escrever código no editor, compilar, executar e então observar, testar e depurar
  • Em Lisp, primeiro se inicia um processo Lisp, conecta-se ao REPL e carrega-se o projeto dentro desse processo
  • REPL é a sigla de Read-Eval-Print Loop, um ambiente interativo que avalia código e mostra imediatamente o resultado
    • Ele funciona como uma janela para observar o processo Lisp que está executando o programa atual
  • Desenvolvedores Lisp continuam avaliando código dentro do processo em execução e verificam os resultados diretamente no REPL
    • Testes de funções individuais
    • Consultas a banco de dados
    • Inspeção de variáveis
    • Tarefas como depuração também podem ser feitas no REPL
  • O processo Lisp não precisa parar e pode permanecer vivo por semanas
  • Funções, macros e variáveis continuam sendo definidas e redefinidas no ambiente interno, chamado environment; esse fluxo é chamado de desenvolvimento guiado por REPL

Hot reloading em Lisp

  • No frontend web, é comum a experiência de código JavaScript sendo hot reloaded ao salvar arquivos ou atualizar a página
  • O hot reloading comum depende de ferramentas e técnicas separadas, como monitoramento de arquivos, recompilação dos módulos alterados e injeção de mudanças via WebSocket
  • Nem todas as stacks de software oferecem hot reloading no mesmo nível
    • Alguns frameworks de backend exigem recompilação
    • Ambientes de desenvolvimento desktop e mobile têm situações variadas
    • Linguagens de baixo nível exigem compilação completa a cada vez
  • Em Lisp, hot reloading não é uma ferramenta separada, mas algo que surge naturalmente ao avaliar código no ambiente vivo
    • Ao redefinir símbolos, funções, dados e macros passam a usar a nova definição
    • Diminui a necessidade de parar e compilar, executar testes separadamente ou usar um depurador especial
  • Programar em Lisp é mais próximo de evoluir um programa do que simplesmente montá-lo

Software extensível

  • A extensibilidade da linguagem e o sistema vivo facilitam a criação de software extensível em Lisp
  • Programas desktop comuns são estendidos por meio de plugins ou linguagens de scripting
    • O desenvolvedor precisa projetar e manter o sistema de extensões
    • O usuário precisa aprender uma API específica para escrever plugins ou extensões
  • Em Lisp, ao encapsular comportamentos com macros e torná-los fáceis de reutilizar, naturalmente se acaba escrevendo uma DSL
  • Para tornar um software extensível, basta permitir que usuários usem a DSL criada internamente
  • No exemplo de um servidor CMS, é possível expor aos usuários uma macro html para geração de páginas web server-side
    • O usuário pode usar variáveis Lisp, laços dolist e formatação de strings com format exatamente como são
    • Não é necessário aprender uma sintaxe separada como {{ user_name }} ou {% for %}, típica de linguagens de template tradicionais
  • Essa DSL herda diretamente as capacidades de condicionais, recursão, funções de ordem superior e depuração passo a passo pelo REPL de Lisp
  • No exemplo de um software de gráficos matemáticos, é possível expor uma DSL para fórmulas e desenho, permitindo que o usuário desenhe curvas e pontos com pouco código

Casos reais e a posição de Lisp

  • O AutoCAD usa AutoLISP para automatizar tarefas repetitivas e gerar geometrias complexas
  • Emacs é um editor de texto implementado em grande parte em seu próprio dialeto de Lisp, garantindo alta extensibilidade
    • Leitor de PDF
    • Navegador web
    • Cliente de e-mail
    • Leitor de RSS
    • Multiplexador de terminal
    • Cliente Git
    • Player de música
    • Cliente de chat
    • Planilha
    • Cliente IRC
    • São listados até casos de extensão para gerenciador de janelas de desktop
  • R. M. Stallman avalia Lisp como uma linguagem que permite compreender o significado de uma linguagem de programação poderosa e elegante
  • A expectativa de que Lisp um dia fosse amplamente usada como linguagem padrão não se concretizou, e parece improvável que isso aconteça no futuro
  • Ainda assim, Lisp sobrevive desde a década de 1960 e se tornou a linguagem de programação em uso ativo mais antiga depois de Fortran
  • O valor de Lisp não vem de um recurso individual como extensibilidade, ambiente interativo ou REPL, mas da combinação deles

1 comentários

 
GN⁺ 3 시간 전
Opiniões do Hacker News
  • A programação existe sobre uma tensão entre o lado luminoso e o lado sombrio
    O lado luminoso tenta impedir que o programador cometa erros: elimina goto, adiciona tipagem estática e torna bugs impossíveis de expressar
    O lado sombrio dá poder ao programador: libera macros, sobrecarga de operadores, código auto modificável e até regex de várias linhas à vontade
    É interessante que Lisp seja claramente do lado sombrio, no sentido de que o programador pode fazer qualquer coisa, e ainda assim seja respeitada também pelos programadores do lado luminoso. Talvez seja porque a simplicidade da linguagem a faz parecer platônica e quase incapaz de se corromper, ou talvez porque Lisp seja tão pura que abranja os dois lados, como um deus que criou o universo da programação

    • Adaptando algo que já vi no debate entre tipagem dinâmica e estática, linguagens permissivas dão poder ao programador que desenvolve sozinho, mas no desenvolvimento em equipe são necessárias linguagens mais restritivas
      Você não pode escolher com quem vai trabalhar, nem o nível de habilidade dentro da equipe. Quando colegas menos experientes cometem erros, quanto mais permissiva for a linguagem, maiores podem ser as consequências
      É por isso que revisão de código é necessária, mas aí você transforma programadores experientes em professores. Parte do tempo gasto elevando o nível de outros programadores é desperdiçado porque programadores entram e saem o tempo todo. A IA pode mudar essa dinâmica
      No fim, o ponto central é manter equipes pequenas e altamente qualificadas, mas isso não é fácil por causa da política corporativa. Por exemplo, muitas vezes se sugere adicionar mais desenvolvedores por causa do fator ônibus ou para encurtar o prazo de lançamento
    • Lisp em geral tem coleta de lixo precisa, então também dá para vê-la como mais próxima do lado luminoso. Se contarmos verificações de tipo em tempo de execução como segurança de tipos, ela também é bastante type-safe
      Sistemas de alta confiabilidade também são escritos em Erlang, que, se você semicerrar os olhos, pode ser visto como outro dialeto de Lisp. Existe até o LFE, uma versão baseada em S-expressions, um Erlang com cara de Lisp
      O ponto central da confiabilidade de Erlang não é um nível excepcional de prevenção de erros, e sim a recuperação de erros. Gosto dessa classificação entre luz e trevas
    • Acho que uma grande característica do Lisp é que, tirando a atribuição de variáveis mutáveis, ele é basicamente declarativo e não um paradigma imperativo
      Mesmo sem tipagem estática e com macros que podem virar bagunça, a linha de base do pensamento declarativo e funcional é muito mais forte, então o ponto de partida para pensar e raciocinar sobre programas é melhor e mais claro
    • Fico curioso em que lado colocariam Malbolge. E Python, Haskell, Idris, Clean, C e K?
      Essa divisão entre luz e trevas parece simplificar demais. O equilíbrio das linguagens de programação é um tema muito complexo e interessante
      Depois de experimentar várias linguagens por anos, no fim tudo dependia do contexto de uso, do gosto pessoal e dos objetivos. Não existe “a melhor linguagem de programação de todos os tempos”, e também não existe “a melhor para programação do lado luminoso” ou “a melhor para programação do lado sombrio”
      Na pintura artística também não existe algo como lado luminoso ou sombrio. Certamente há quadros maiores que outros, mas não existe “o melhor quadro” que todo mundo ache o mais belo em comparação com todos os demais
      https://en.wikipedia.org/wiki/Malbolge
      https://en.wikipedia.org/wiki/Clean_(programming_language)
      https://en.wikipedia.org/wiki/K_(programming_language)
    • Acho que misturaram luz e trevas. O lado sombrio é o de restrição e disciplina, como se o criador soubesse o que é melhor; o lado luminoso é o que confia poder às pessoas
  • Nos últimos meses surgiram novos editores e ferramentas excelentes, então não vale deixar passar
    Mine é um app de download único que oferece tudo o que você precisa para experimentar um fluxo de desenvolvimento interativo e incremental, incluindo hot reloading e depuração instantânea. É para CL e Coalton
    https://coalton-lang.github.io/20260424-mine/
    OLIVE é um plugin artesanal para VSCode, e ICL é um novo REPL para terminal e navegador com recursos avançados. Como bônus, também há um kernel JupyterLite baseado em JSCL, então ele roda 100% no navegador
    Tudo pode ser visto aqui: https://lispcookbook.github.io/cl-cookbook/editor-support.ht...

    • Não consigo encontrar o código-fonte do mine. Tem algum link?
  • Parece que o site está com um bug de syntax highlighting. Os trechos de código incluídos no texto principal aparecem em preto, e ao selecionar com o mouse dá para ver o texto de verdade
    Vejo o mesmo bug no Chrome de desktop e no Safari no iPad

    • É um caso clássico de sempre definir também a cor do texto quando se muda a cor de fundo. Senão, você acaba com blocos de texto preto sobre fundo preto. Se o navegador estiver em modo escuro, provavelmente vai parecer normal
      Uma solução alternativa é abrir um dos elementos de código no depurador do navegador e adicionar color: white ao estilo :not(pre) > code
    • Corrigi o erro assim que li o comentário
    • Chrome no macOS? No Firefox, Chromium e Brave no Linux, os blocos de código aparecem normalmente. Acho que todos usam Qt
    • O mesmo problema aparece tanto no Firefox quanto no Chrome
    • No começo achei que o texto tivesse sido censurado
  • Há muitos textos exaltando as vantagens de Lisp. Eu também gostaria de ver textos que critiquem com calma Lisp, suas ideias e seu lugar no ecossistema de linguagens
    No fim, esses textos e os artigos do PG referenciados aqui acabam ficando perto de “quem sabe, sabe”. Entendo o apelo e também entendo as afirmações explícitas e implícitas que este texto faz
    Programação de computadores amadureceu bastante nos últimos 60 anos. Gostaria de ver mais textos com análises mais bem refletidas

    • Como linguagem, Lisp é excelente, mas o ecossistema é limitado. Há dois defeitos. Um é que ela é aberta demais, então, sem talento e disciplina, você pode cair na toca de coelho de hacks divertidos em Lisp e não terminar o trabalho de verdade. Outras linguagens também têm esse problema, mas acho que em Lisp ele é pior
      O segundo defeito é que ela consome anos do tempo de pessoas suficientemente competentes, mas o resultado não justifica esse tempo investido. Algo como Python também basta. Acho que teria sido melhor usar a maior parte do tempo que investi em Lisp em outra coisa
    • A expressão “programação amadureceu bastante” soa como se Lisp tivesse parado em algum ponto do passado
      Clojure, ClojureScript, Clojure-Dart, Fennel, Jade, Jank, Jolt e Coalton são linguagens relativamente recentes e ainda estão evoluindo. Só de cabeça lembro dessas, e há muito mais
      Em certo sentido, Lisp não é uma linguagem de programação, mas uma ideia. Ela influenciou quase toda linguagem de programação que usamos hoje e continua influenciando. É difícil criticar uma ideia “com calma”; é parecido com criticar teoria dos grupos. O que dá para discutir são os prós e contras de implementações específicas dessa ideia
    • A maldição de Lisp: https://www.winestockwebdesign.com/Essays/Lisp_Curse.html
    • Em uma linguagem atual, concorrência padronizada é requisito básico. CL não tem async/await padronizado nem um modelo de concorrência padronizado. O padrão não é atualizado desde 1995, então isso provavelmente nunca vai acontecer
    • Parece haver em Lisp algo como a direção da maçaneta
      Para algumas pessoas, isso é mentalmente atraente. Dá vontade de comparar com um gênero musical realmente de nicho. Poucas pessoas gostam, mas quem gosta, gosta com muita intensidade. É como Stockhausen da programação
      No fundo é um fandom. Um fandom surpreendentemente duradouro, mas que nunca rompeu para o grande público e provavelmente não vai romper. Agora a posição é ainda pior. Se você está armado com LLMs e não se importa tanto assim com o código em si, por que mandaria escrever em Lisp?
  • Faço Lisp há tempo demais e acabo não valorizando adequadamente essas funcionalidades. Aí, quando vejo com choque e horror as pessoas sabotando a si mesmas em linguagens populares e economicamente menos arriscadas, de repente volto a sentir o valor de Lisp

  • Em ambiente comercial, linguagens específicas de domínio são um antipadrão. Alguém cria uma linguagem específica de domínio mal documentada que só ele entende e depois vai embora
    Os programadores que vêm depois precisam se esforçar para decifrar essa linguagem estranha. Quase sempre é melhor usar estruturas e recursos da linguagem padrão. Código é lido mais vezes do que escrito

    • Cada vez que você cria uma função, de certo modo está criando sua própria linguagem especializada naquele domínio
      Na prática, o que você provavelmente rejeita é a falta de transparência referencial. Para ser justo, isso é um problema em muitas linguagens, inclusive Lisp
    • Em Lisp, os programadores que vêm depois podem simplesmente olhar a expansão do macro. Com ferramentas modernas, se quiserem, podem até ver isso dentro do editor, como texto alternativo no mesmo arquivo-fonte, enquanto estão conectados a um ambiente Lisp em execução
      Um bom exemplo é o próprio macro LOOP de Common Lisp. Se o uso que alguém fez for difícil de entender, você pode pedir ao Lisp para expandi-lo em chamadas primitivas mais verbosas sem LOOP, e não precisa entender LOOP de forma alguma. Na prática, dá até para substituir o form LOOP pelo código expandido
      Essa é uma diferença fundamental em relação à maioria das outras linguagens, nas quais o código de linguagem específica de domínio muitas vezes é, na prática, uma estrutura de dados interpretada
  • Há coisas realmente poderosas e únicas nos Lisps, mas eu preferia que esses textos não continuassem incluindo REPL e hot reloading
    REPL já era requisito básico em linguagens interpretadas, e até em algumas compiladas, desde muito tempo atrás. Hot reloading não é algo único, nem particularmente amplamente usado. Como é preciso lidar com estado e patches, existe um motivo para redefinir tudo inteiro ser considerado boa prática quando se quer facilitar o raciocínio

    • Quando eu estava aprendendo Clojure, também pensava assim. Só bem mais tarde percebi que, quando o pessoal de Lisp fala em REPL, normalmente não está falando de uma CLI interativa para avaliar comandos com facilidade, mas da capacidade de conectar o programa a uma sessão viva para avaliar rapidamente forms no editor de texto dentro do contexto da aplicação em execução
      Isso permite um desenvolvimento muito mais interativo do que em outras linguagens interpretadas
    • Em Dart/Flutter, hot reload é muito amplamente usado. Quando você escreve código de UI em um framework de estilo reativo, e a renderização da UI parece “gerar uma UI nova do zero a cada frame”, fica mais fácil ter intuição sobre o que é recarregado e o que não é
      Claro, não é perfeito. Mas funciona muito bem para o tipo de mudança que você faz ao iterar sobre a experiência do usuário
    • Concordo em geral
      Ainda assim, vale destacar que boa parte dos aspectos avançados de hot reloading já foi tratada em Common Lisp. O mesmo vale para o fato de que o REPL costuma ser usado menos como lugar para digitar diretamente e mais como uma interface muito poderosa entre a imagem em execução e as ferramentas
      Mesmo assim, concordo que só o fato de essas coisas existirem não as torna tão especiais hoje em dia
    • Nem todo dialeto de Lisp tem um sistema de hot patch bem desenvolvido. O sistema orientado a objetos precisa ser projetado com cuidado para isso
      O que fazer se uma definição de classe é substituída por um reload, mas as instâncias existentes continuam lá? Dialetos de Common Lisp têm respostas úteis para esse tipo de pergunta, mas nem todo Lisp que você encontrar necessariamente tem
      Bash também suporta hot reloading. Já desenvolvi módulos atualizados no próprio lugar assim, usando source
      $ . /path/to/script.sh
    • Em geral, a dor de lidar com hot patch depende muito da estrutura do codebase e do motivo pelo qual você quer fazer hot patch
      Se for uma arquitetura estilo array structure ou uma correção de erro lógico em runtime, normalmente não é nada demais
      A utilidade é limitada, mas, se você parte da perspectiva de que vai usar isso bastante, essa limitação começa a desaparecer bem rápido
  • Pessoalmente, já passei dessa fase. Já usei Common Lisp, Forth e Haskell, e li On Lisp e Let Over Lambda com prazer
    Hoje acho que a melhor linguagem de programação é o Triage Calculus do Barry Jay. É mais próxima da lógica combinatória ou do cálculo lambda sem tipos
    Mas, diferente do cálculo lambda, o Triage Calculus tem citação embutida e introspecção parecidas com CAR e CDR do Lisp, então é fácil adicionar verificação de tipos. Por isso, no TC, se você conseguir criar a abstração certa, pode ter qualquer sintaxe que quiser. É realmente a linguagem de programação definitiva
    O TC mostra que a distinção entre, de um lado, recursos e sintaxe da linguagem de programação e, de outro, definições de funções/API da biblioteca padrão ou do código do usuário é uma falsa dicotomia. Sempre que você escreve código pensado para reutilização, está adicionando algo à linguagem. Tudo isso pode ser expresso como algum termo do TC
    Toda discussão sobre sintaxe é uma questão de hábito. Dito isso, o único recurso de sintaxe que eu realmente queria no TC é a abstração let over lambda, porque organizar combinadores manualmente é um pouco incômodo

  • Texto bem escrito
    Antigamente eu achava que um dia todo mundo usaria Lisp e, quando tivéssemos ótimas ferramentas e bibliotecas, tudo ficaria maravilhoso. Na verdade, meu plano para a aposentadoria era criar bibliotecas de alta qualidade para linguagens Lisp para ajudar a acelerar esse processo
    A ascensão da IA será o fim desse sonho? Será que, mais uma vez, resolvemos o problema só acrescentando mais tralha? Em vez de uma IA superinteligente escrever código na melhor linguagem de programação possível, será que vamos simplesmente despejar um monte de código Python e insistir até funcionar?
    Não sei se isso importa. Eu só queria um mundo com elegância

    • Sempre me perguntei o que poderia ter acontecido com o primeiro “Interface Builder”
      “Jean-Marie Hullot criou o ‘SOS Interface’ para o Macintosh enquanto trabalhava no INRIA, escrito em Lisp, e este foi o primeiro ‘interface builder’ moderno”
      https://denninginstitute.com/itcore/userinterface/GUIHistory...
      Fico imaginando como teria sido se isso tivesse virado um produto Mac dominante, em vez de ir para a NeXT e ser usado com Objective-C
      LISP combinava bem comigo. Na disciplina de linguagens comparadas na faculdade, fui a única pessoa que fez todos os exercícios de LISP, e aprender LISP também me ajudou bastante a usar TeX
      O que eu queria era um toolkit GUI nativo ou multiplataforma decente e open source, além de uma forma fácil de distribuir projetos como código compilado executável independente
    • A linguagem nunca é a resposta. Lisp não é superior a Visual Basic. Ambas são Turing-completas
      Seres humanos conseguem expressar boas ideias em ambas. É melhor pensar na linguagem como uma ferramenta. Uma furadeira de impacto não é superior a uma furadeira rotativa simples. Para construir um deck, a furadeira de impacto pode ser mais adequada, mas as duas dão conta do recado
  • Achei que este fosse https://web.archive.org/web/20120106121645/http://wiki.alu.o...
    Isso me trouxe lembranças de acompanhar comp.lang.lisp. Era quase um drama. Havia não só magia da programação, mas também muitas figuras marcantes e acontecimentos