2 pontos por GN⁺ 2024-06-03 | 1 comentários | Compartilhar no WhatsApp
  • O Spring Lisp Game Jam 2024 recebeu 48 jogos, batendo um novo recorde, e os trabalhos inscritos se dividiram claramente entre a abordagem de colocar Lisp por cima e a de fazer a própria stack em Lisp
  • A abordagem de cobertura coloca Lisp como uma camada de scripting sobre programas baseados em C/Rust/Lua, produzindo resultados rapidamente, mas ficando fortemente presa à linguagem estática e ao toolchain por baixo
  • A abordagem de bolo escreve a maior parte do programa em Lisp e minimiza o uso de C FFI, ganhando controle mais profundo, mas aumentando o custo de implementação de bibliotecas, escrita de wrappers e distribuição na Web
  • No Game Jam, Fennel+love2d e S7+raylib ficam mais próximos da cobertura; Guile+Chickadee é bolo; e Hoot+HTML5 canvas fica mais perto de bolo graças ao toolchain Wasm baseado em Scheme
  • Quanto maior a presença de Lisp, maiores são as possibilidades de live hacking, segurança de memória, redução das fronteiras Lisp/C e hackeabilidade; projetos como Guix, Trial e Pre-Scheme também apontam nessa direção

Situação das submissões no Spring Lisp Game Jam 2024

  • O Spring Lisp Game Jam 2024 terminou há uma semana e recebeu 48 jogos, estabelecendo um novo recorde para a jam
  • Depois disso, os participantes passaram uma semana jogando e avaliando os jogos uns dos outros
  • A distribuição dos trabalhos por linguagem foi a seguinte
    • Guile: 15, 31%
    • Fennel: 10, 21%
    • Clojure: 5, 10%
    • Common Lisp: 5, 10%
    • Racket: 4, 8%
    • Elisp: 4, 8%
    • S7: 3, 6%
    • Kawa: 1, 2%
    • Owl: 1, 2%
  • Participação do Guile: {p:31}
  • O motivo de não agrupar as implementações de Scheme em uma única categoria scheme é que a especificação de Scheme é pequena, e Guile, Racket, S7 e Kawa são implementações com propósitos diferentes
  • Nesta jam, Guile teve o maior número de submissões pela primeira vez
    • Dos 15 jogos em Guile, 11 são jogos Web feitos com Hoot
    • Hoot é um compilador de Scheme para WebAssembly em desenvolvimento pelo Spritely Institute
    • 2 desses 11 são projetos oficiais da Spritely
    • O Spritely Institute pediu, antes do início da jam, que as pessoas tentassem criar jogos com Hoot, e muitos participantes aceitaram
  • Normalmente, a linguagem mais popular nessa jam é Fennel, um Lisp que compila para Lua
  • Os 3 jogos que usaram S7 também servem como exemplos relacionados às formas de usar Lisp no desenvolvimento de jogos

Usando Lisp como cobertura

  • O padrão de cobertura é uma abordagem que coloca Lisp como uma linguagem de scripting sobre um “bolo” feito em linguagens estáticas como C ou Rust
  • Normalmente, isso envolve embutir um interpretador Lisp dentro de um programa maior
  • Se você quiser escrever as partes de alto nível da aplicação em Lisp, esse pode ser o caminho mais rápido
    • É preciso ter um interpretador ou compilador adequado
    • Também é necessário haver uma forma de adicionar os hooks de que a aplicação precisa
  • Se a parte principal do programa for escrita em C ou Rust, é possível compilá-la para WebAssembly com emscripten e distribuí-la na Web
  • Dá para obter rapidamente um resultado satisfatório, mas isso cria um acoplamento forte à linguagem estática e ao respectivo toolchain
  • Exemplos representativos incluem
    • S7 é um Scheme embutível
    • Guile também pode ser usado para estender programas em C, mas normalmente faz link dinâmico com libguile, em vez de colocar o interpretador dentro do executável
    • Fennel aproveita aplicações existentes com pontos de extensão em Lua e compila uma linguagem parecida com Lisp para Lua

Usando Lisp como bolo

  • O padrão de bolo é uma abordagem que implementa o máximo possível da stack de software em Lisp
  • Em vez de colocar Lisp dentro de um programa não Lisp, a maior parte do programa é escrita em Lisp
  • Quando necessário, chama bibliotecas compartilhadas por meio de uma interface de funções externas (FFI), mas é melhor minimizar esse uso
  • Leva mais tempo até produzir resultados
    • É preciso implementar por conta própria bibliotecas que não existem na implementação Lisp escolhida
    • Para bibliotecas compartilhadas em C inevitáveis, é preciso escrever wrappers
    • Como o projeto não se torna facilmente um alvo do emscripten, a distribuição na Web fica mais difícil
  • Essa abordagem se relaciona com o debate clássico de embed vs. extend
  • Guile pode ser usado como cobertura, mas mostra mais força quando usado como bolo
    • A visão inicial do Guile era adicionar um interpretador Scheme para tornar outros programas parecidos com o Emacs
    • A boa prática atual é escrever o programa em Scheme desde o início
  • Common Lisp também é um bom exemplo da abordagem de bolo
    • Implementações como SBCL oferecem uma boa C FFI
    • Como ele pode compilar para executáveis nativos eficientes, reduz as situações em que alguém ficaria tentado a usar C por desempenho

Cobertura e bolo vistos por casos do Game Jam

  • Fennel + love2d

    • love2d há muito tempo é uma escolha popular para desenvolvimento de jogos por uma pessoa ou equipes pequenas
    • love2d é um programa em C++ com um interpretador Lua embutido, então é um bom alvo para Fennel
    • Como a maioria das distribuições Linux empacota o love2d, é fácil executar arquivos .love nativamente
    • Graças ao emscripten, jogos love2d também podem ser distribuídos na Web
    • Por isso, a maioria dos jogos em Fennel usa love2d
    • ./soko.bin e Gnomic Vengeance usam essa stack
    • Fennel+love2d é um exemplo completo de Lisp as icing
    • Fennel fica no topo da stack, e praticamente não há caminho para espalhar Lisp para as camadas inferiores
    • Até agora, é a stack de desenvolvimento de jogos em Lisp mais bem-sucedida
  • S7 + raylib

    • Nesta jam, dois jogos, GhostHop e Life Predictor, usaram a stack S7+raylib
    • Raylib é uma biblioteca em C com bindings para várias linguagens de alto nível, e sua popularidade cresceu nos últimos anos
    • S7 também é implementado em C e pode ser facilmente embutido, então essa combinação é fácil de distribuir na Web com emscripten
    • S7+raylib também é um caso de Lisp as icing, e vale observar se ficará mais popular em jams futuras
  • Guile + Chickadee

    • Chickadee é uma biblioteca de jogos para Guile, e implementa em Scheme quase todas as partes interessantes, incluindo renderização
    • Em jams recentes, dois jogos, Turbo Racer 3000 e Bloatrunner, foram feitos com Chickadee
    • Guile+Chickadee é um caso de Lisp as cake
    • Chickadee envolve algumas bibliotecas em C para tarefas de baixo nível, como carregamento de imagens, áudio e fontes, mas seu próprio código é escrito em Scheme puro
    • A matemática de matrizes e vetores também é toda implementada em Scheme
    • Ele oferece um conjunto de primitivas de renderização comparável ao love2d e ao raylib, também implementado em Scheme
    • Enquanto outras bibliotecas de jogos em Lisp frequentemente usam bibliotecas em C como nanosvg, Chickadee também avançou na implementação de renderização de gráficos vetoriais em Scheme
    • Chickadee forçou os limites do compilador e da máquina virtual do Guile, e o Guile também melhorou nesse processo
    • Porém, como foi desenvolvido em grande parte por uma única pessoa em seu tempo livre limitado, leva bastante tempo para atingir paridade de recursos com bibliotecas de desenvolvimento de jogos mais populares
    • Mesmo no estado atual, ele funciona muito bem para esse propósito
  • Hoot + HTML5 canvas

    • Hoot é um compilador de Scheme para WebAssembly
    • Hoot não compila a VM Guile escrita em C para Wasm com emscripten
    • Em vez disso, implementa um toolchain Wasm completo e um novo backend para o compilador Guile que emite Wasm diretamente
    • Hoot é escrito inteiramente em Scheme
    • Diferentemente de programas em C compilados com emscripten, que têm como alvo o Wasm 1.0 baseado em memória linear, Hoot mira o Wasm 2.0, com tipos de heap gerenciados por GC
    • Graças a essa estrutura, binários Hoot não são distribuídos com um coletor de lixo incluído
    • Por isso, são muito menores que runtimes Lisp compilados com emscripten
    • O binário Wasm de um jogo Hoot tem menos de 2MiB, enquanto o love.wasm de um jogo love2d verificado tinha quase 6MiB
    • Programas Hoot interoperam facilmente com JavaScript
      • Objetos Scheme podem ser passados facilmente para JavaScript
      • Objetos JavaScript também podem ser passados para Scheme
      • Isso porque os objetos dos dois lados são gerenciados no mesmo heap
    • APIs do navegador podem ser acessadas por imports Wasm, então, para jogos, a API HTML5 canvas embutida se torna uma opção fácil de renderização 2D
    • Nesta jam, houve 11 jogos que usaram Hoot, incluindo Cirkoban e Lambda Dungeon
    • Hoot+HTML5 canvas é, em sua maior parte, um bolo espesso com um pouco de cobertura misturada
    • Fazer o Hoot inicializar levou 1 ano e financiamento considerável
    • Sem usar emscripten, foi criado um toolchain próprio, e o compilador Guile também foi estendido
    • Também há um interpretador Wasm que roda sobre a VM Guile
    • Por outro lado, a API canvas é de nível muito alto
    • Uma forma mais próxima de bolo seria chamar WebGL ou WebGPU pela JS FFI do Hoot
    • Os planos futuros apontam para WebGL/WebGPU, e melhorias no Wasm GC são necessárias para viabilizar isso
    • Outro objetivo é portar Chickadee para Hoot, fazendo com que jogos Chickadee possam ser jogados facilmente tanto nativamente quanto no navegador, como jogos love2d

Limites e vantagens da abordagem de bolo

  • A abordagem de bolo também tem limites claros
  • O ambiente moderno não é o mundo das Lisp machines, e até o bolo Lisp mais alto fica, em grande parte, sobre um bolo maior feito em C
  • Sistemas Lisp modernos chegam às camadas inferiores em algum ponto
    • Emacs fica sobre um núcleo em C
    • A VM Guile é escrita em C
    • Hoot roda sobre grandes engines JavaScript baseadas em C++, como V8
    • Jogos Hoot atualmente renderizam com HTML5 canvas, não WebGL/WebGPU
    • Usar OpenGL exige libGL
    • Chickadee usa guile-opengl, que chama libGL via C FFI
    • libpng, FreeType e outros também existem
  • Reescrever tudo em Lisp envolve um grande problema de recursos
  • Ainda assim, recuperar partes da stack de linguagens como C é uma pequena vitória
  • As partes escritas em Lisp são mais fáceis de hackear, e algumas permitem live hacking enquanto o programa está em execução
  • Graças a runtimes gerenciados por GC, geralmente se obtém segurança de memória
  • Reduzir chamadas FFI diminui o overhead de cruzar a fronteira Lisp/C e também aumenta a segurança
  • Quanto maior a presença de Lisp na stack, mais ela se aproxima de bolo em vez de cobertura

Casos de bolo fora dos jogos

  • Guix é um bom exemplo de quão poderosa a abordagem de bolo pode se tornar
  • Guix pegou o modelo de empacotamento funcional do projeto Nix e o reimplementou, substituindo a linguagem Nix por Guile
  • Os motivos foram staging de código, compartilhamento de código e maior hackeabilidade
  • Guix também usa, no lugar do systemd, um sistema init escrito em Guile, e essa escolha veio pelos mesmos motivos
  • No início, Guix era facilmente criticado por reinventar a roda sem motivo, mas, 10 anos depois, a insistência em maximizar o uso de Lisp se tornou central para o sucesso do projeto
  • Ao aprender os idiomas do Guix e um pouco de Guile, usuários ganham uma capacidade poderosa de configurar o sistema operacional como quiserem
  • Guix pode ser visto como a experiência mais próxima de uma Lisp machine em hardware moderno
  • No lado de Common Lisp, o motor de jogos Trial é um caso de implementação de muitas partes em Common Lisp, em vez de envolver bibliotecas em C
  • Projetos como Pre-Scheme dão a esperança de que, algum dia, também seja possível implementar em Lisp as camadas abaixo de runtimes gerenciados por GC
    • Pre-Scheme foi desenvolvido e usado com sucesso no Scheme 48
    • Graças a um grant da NLnet, espera-se uma retomada moderna

A direção de criar mais stack em Lisp

  • A direção está mais próxima de bolo
  • É necessário que mais projetos continuem expandindo os limites do que Lisp pode fazer
  • O mais interessante no Lisp Game Jam não são os jogos em si, mas os pequenos avanços para recuperar um pedaço do bolo do velho e seco C
  • No desenvolvimento de jogos com Guile, a intenção é continuar empurrando os limites com o projeto Chickadee
  • A conclusão não é reescrever em Rust, mas sim reescrever em Lisp

1 comentários

 
GN⁺ 2024-06-03
Opiniões do Hacker News
  • É ainda mais gratificante porque hoje em dia quase não se veem textos que comparem abordagens de software de forma objetiva
    Mesmo tentando procurar esse tipo de texto, hoje muitas vezes os resultados de busca não conseguem superar o spam de SEO
    Janet parece ter sido feita para jogos e também parece ter, surpreendentemente, muitos elementos “baterias incluídas”, como servidor web ou gráficos, então fiquei um pouco surpreso por não ver jogos usando Janet
    Acho que é uma linguagem que vale a pena examinar no lado de Lisp e jogos

  • É bom ver o s7 recebendo atenção
    Usei-o como Scheme no Scheme for Max, uma extensão open source que coloca um interpretador Scheme no ambiente de música computacional Max/MSP; ele fica em algum ponto entre Guile, Clojure e Common Lisp, mas é muito pequeno e fácil de embutir
    Também gosto do fato de ter uma licença BSD, muito mais permissiva que a do Guile
    Se você gosta de macros de Common Lisp com ambientes de primeira classe, há uma boa chance de também gostar do s7
    Também foi muito fácil usá-lo em WASM, e é assim que o estamos usando em um projeto de educação musical
    Criar funções genéricas para chamar funções JS a partir de Scheme e, no sentido inverso, chamar Scheme a partir de JS também não foi difícil, então o fluxo todo ficou bem suave

    • Mais um voto para o s7
      Embutimos com sucesso s7 e SQLite como engine, exceto pelos gráficos, em apps nativos para iOS e Android
      Era muito rápido, tinha uma boa FFI, era estável e pequeno, e tivemos grandes ganhos com código compartilhado entre apps móveis, testes unitários extremamente rápidos e um ferramental limpo
      No fim, em mobile migramos para o Fennel, um Lisp mais prático
      Enquanto quase só a nossa equipe usava s7 em mobile, Lua era muito mais comum como linguagem de extensão mobile, e o estado da compatibilidade com r7rs Scheme também pesou
      Como usávamos Guile no desenvolvimento desktop e s7 na distribuição, esbarrávamos com frequência em incompatibilidades sutis, por exemplo a ordem de avaliação de parâmetros
      Tanto s7 quanto Fennel têm projetos e comunidades excelentes
  • Recomendo olhar com atenção o Spritely Institute, especialmente o blog
    Não quero dar spoiler sobre o que eles fazem, mas é um tema e uma instituição que valem um mergulho profundo
    Passei mais de 10 horas lendo o blog, links relacionados e projetos
    https://spritely.institute/archive/

    • Parece um lugar que atrai gente boa e faz coisas bem legais
  • A síntese “não vivemos no mundo das máquinas Lisp, mas no mundo de PDP-11s glorificados” é marcante
    Mas fico curioso se também existe alguma “cobertura” para usar com SDL

    • É difícil chamar CPUs tradicionais de PDP-11s glorificados
      Elas são parecidas no sentido de serem máquinas de von Neumann que usam memória sem tags, mas por volta de meados dos anos 80, quando microprocessadores de 32 bits se tornaram comuns e a tecnologia de compiladores Lisp evoluiu para isso, CPUs tradicionais começaram a ultrapassar as máquinas Lisp
      A parte de “memória sem tags” também pode não continuar valendo, considerando tendências como CHERI, e há espaço para um grande retorno de arquiteturas no estilo LispM
    • Eu gostava muito do PDP-11
      Por alguns anos tive um PDP-11/45 na sala de estar e, depois, para economizar espaço, troquei por alguns H-11 LSI-11/2 com dois drives de disquete de 8 polegadas
      O PDP-11 não é apenas uma espécie de protoplasma do Unix; ele também era conceitualmente bem projetado
      Assim como preferimos rotinas que “cabem em uma tela”, o pequeno espaço de endereçamento direto do PDP-11 induzia módulos não grandes demais e incentivava a modularidade
      Hoje ele é insuficiente? Claro, e especialmente doloroso para big data
      Ainda assim, há razões conceituais para o sucesso do PDP-11 e para ele ainda deixar vestígios hoje
    • Ironicamente, agora estamos indo para máquinas C com tagging de memória em hardware
      Porque não há outra forma de consertar C, e há código demais que não será reescrito
    • Gostei mais do final: “Reescrever em Rust? De jeito nenhum! Reescreva em Lisp!
    • Lisp não é adequado para CPUs modernas por causa da hierarquia de memória
      Lisp lida principalmente com listas, e listas podem seguir ponteiros espalhados pela memória
      Em CPUs antigas isso não era um problema, porque a memória em geral tinha o mesmo tempo de acesso aleatório, mas CPUs modernas não conseguem seguir ponteiros de memória na mesma velocidade e precisam obedecer a regras de localidade para desempenho
      Por isso, algoritmos que usam coisas como arrays em C ou Fortran inevitavelmente serão sempre mais rápidos que versões baseadas em listas em Lisp
  • Estou animado com o progresso recente do Guile Scheme
    Diferente da última vez que olhei, ele deixou de ser uma linguagem interpretada e passou a ter um compilador de verdade, e agora também consegue compilar para WASM com o Hoot
    Tenho familiaridade com Clojure, uLisp e Common Lisp, mas o Guile Scheme parece remover boa parte do excesso do Common Lisp; e, especialmente se Guix e Shepard se consolidarem, quero ter à mão um Lisp compilado
    Fico curioso se há bons materiais para aprender Guile Scheme de forma eficaz além de Little Lisper e SICP

  • Li com interesse porque estou pensando em usar Guile em algo que vou criar daqui para frente
    O trabalho no lado de WASM também parece ter ficado bom

  • Recentemente fiz um protótipo de boss fight 3D em Clojure: https://prototype-game.pages.dev

    • Excelente
      Antigamente eu odiava qualquer forma de desenvolvimento web, mas ClojureScript realmente tornou isso prazeroso, e gostaria que fosse mais usado
    • No mobile os controles não funcionam
      Vou tentar de novo depois, quando estiver na frente do PC
    • Legal
      Fico curioso para saber quais bibliotecas você está usando
    • Falando com carinho pelo que você criou, isso me lembrou Avatar - Legends of the Arena
      Era um dos jogos de que eu realmente gostava na infância
      https://www.youtube.com/watch?v=dcJFldES9dg
  • Embora eu tenha pedido para colocar na tabela, talvez isso já seja notícia velha
    Referências:
    https://lispy-gopher-show.itch.io/logos-lisp-legend/devlog/7...
    https://itch.io/post/10013482

  • Janet ficou de fora, apesar de existir https://ianthehenry.com/posts/janet-game/
    O ano de copyright do texto aparece como 1899–1907, mas é uma pena que ele não pareça vintage

    • Parece que Janet ficou de fora
      Para scripting, voltei para Fennel, porque dá para usar diretamente muito mais bibliotecas Lua e ele roda sem problemas praticamente em qualquer lugar, até no a-Shell do iPad
    • Considerei Janet muito a sério, mas não vi um caminho direto até meu objetivo de usar apps web e WebGL (ThreeJS)
      Havia materiais de referência úteis nesta thread, e talvez eu tente de novo mais tarde: https://janet.zulipchat.com/#narrow/stream/409517-help/topic...
      Mesmo tendo um caminho de produção já comprovado, consegui fazer algo jogável por pouco dentro do prazo, e “jogável” já é um termo meio generoso
  • Fico muito curioso para saber que tipo de jogo foi feito em Emacs Lisp
    Não é a primeira opção que vem à mente quando penso em programação de jogos