2 Punkte von GN⁺ 2024-06-03 | 1 Kommentare | Auf WhatsApp teilen
  • Beim Spring Lisp Game Jam 2024 wurden 48 Spiele eingereicht – ein neuer Rekord. Die Beiträge teilen sich deutlich in zwei Ansätze: Lisp als Schicht obenauf und Lisp als Stack selbst
  • Der Zuckerguss-Ansatz legt Lisp als Scripting-Schicht über Programme auf Basis von C/Rust/Lua und liefert schnell Ergebnisse, ist aber stark an die darunterliegenden statischen Sprachen und Toolchains gebunden
  • Der Kuchen-Ansatz schreibt den Großteil des Programms in Lisp und minimiert C-FFI, was tiefere Kontrolle ermöglicht, aber höhere Kosten bei Bibliotheksimplementierung, Wrappern und Web-Deployment verursacht
  • Im Game Jam sind Fennel+love2d und S7+raylib eher Zuckerguss, Guile+Chickadee ist Kuchen, und Hoot+HTML5 canvas ist dank einer Scheme-basierten Wasm-Toolchain näher am Kuchen
  • Je größer der Lisp-Anteil wird, desto mehr nehmen Live-Hacking, Speichersicherheit, weniger Lisp/C-Grenzen und Hackbarkeit zu; Projekte wie Guix, Trial und Pre-Scheme zeigen dieselbe Richtung

Einreichungen beim Spring Lisp Game Jam 2024

  • Der Spring Lisp Game Jam 2024 endete vor einer Woche, und mit 48 eingereichten Spielen wurde ein neuer Jam-Rekord aufgestellt
  • Danach spielten und bewerteten die Teilnehmenden eine Woche lang gegenseitig ihre Spiele
  • Die Verteilung der Einreichungen nach Sprache sieht wie folgt aus
    • 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 %
  • Anteil von Guile: {p:31}
  • Der Grund, Scheme-Implementierungen nicht in einer einzigen Kategorie scheme zusammenzufassen, ist, dass die Scheme-Spezifikation klein ist und Guile, Racket, S7 und Kawa Implementierungen mit unterschiedlichen Zielen sind
  • Bei diesem Jam hatte Guile erstmals die meisten Einreichungen
    • 11 der 15 Guile-Spiele sind Web-Spiele, die mit Hoot erstellt wurden
    • Hoot ist ein Scheme-to-WebAssembly-Compiler, der vom Spritely Institute entwickelt wird
    • 2 der 11 sind offizielle Spritely-Projekte
    • Das Spritely Institute hatte vor Beginn des Jams dazu aufgerufen, Spiele mit Hoot zu bauen, und viele Teilnehmende folgten dem
  • Normalerweise ist Fennel die beliebteste Sprache in diesem Jam; Fennel ist ein Lisp, das nach Lua kompiliert
  • Auch die drei Spiele mit S7 sind Beispiele dafür, wie Lisp in der Spieleentwicklung eingesetzt wird

Lisp als Zuckerguss verwenden

  • Das Zuckerguss-Muster ist ein Ansatz, bei dem Lisp als Scripting-Sprache auf einen „Kuchen“ aus statischen Sprachen wie C oder Rust gelegt wird
  • Meist wird ein Lisp-Interpreter in ein größeres Programm eingebettet
  • Wenn man die High-Level-Teile einer Anwendung in Lisp schreiben möchte, kann das der schnellste Weg sein
    • Man braucht einen passenden Interpreter oder Compiler
    • Außerdem muss es eine Möglichkeit geben, die nötigen Hooks in die Anwendung einzubauen
  • Wenn der Hauptteil des Programms in C oder Rust geschrieben ist, kann man ihn mit emscripten nach WebAssembly kompilieren und im Web deployen
  • Man kann schnell zufriedenstellende Ergebnisse erzielen, ist aber stark an die statische Sprache und deren Toolchain gekoppelt
  • Typische Beispiele sind
    • S7 ist ein einbettbares Scheme
    • Guile kann ebenfalls zur Erweiterung von C-Programmen genutzt werden, wird aber normalerweise dynamisch gegen libguile gelinkt, statt den Interpreter in die ausführbare Datei zu packen
    • Fennel nutzt bestehende Anwendungen mit Lua-Erweiterungspunkten und kompiliert eine Lisp-artige Sprache nach Lua

Lisp als Kuchen verwenden

  • Das Kuchen-Muster ist ein Ansatz, bei dem möglichst viel des Software-Stacks in Lisp implementiert wird
  • Statt Lisp in ein Nicht-Lisp-Programm einzubauen, wird der Großteil des Programms in Lisp geschrieben
  • Bei Bedarf werden Shared Libraries über ein Foreign Function Interface (FFI) aufgerufen, doch dieser Einsatz sollte möglichst gering bleiben
  • Bis Ergebnisse sichtbar werden, dauert es länger
    • Bibliotheken, die in der gewählten Lisp-Implementierung fehlen, müssen selbst implementiert werden
    • Für unvermeidbare C-Shared-Libraries müssen Wrapper geschrieben werden
    • Web-Deployment wird schwieriger, weil das Projekt nicht ohne Weiteres ein emscripten-Ziel ist
  • Dieser Ansatz berührt die klassische Debatte embed vs. extend
  • Guile kann auch als Zuckerguss dienen, spielt seine Stärken aber eher als Kuchen aus
    • Die ursprüngliche Vision von Guile war, andere Programme durch Hinzufügen eines Scheme-Interpreters Emacs-ähnlich zu machen
    • Die heutige gute Praxis ist, Programme von Anfang an in Scheme zu schreiben
  • Auch Common Lisp ist ein gutes Beispiel für den Kuchen-Ansatz
    • Implementierungen wie SBCL bieten ein gutes C-FFI
    • Sie können zu effizienten nativen Executables kompiliert werden, was Situationen reduziert, in denen man aus Performance-Gründen zu C greifen möchte

Zuckerguss und Kuchen anhand von Game-Jam-Beispielen

  • Fennel + love2d

    • love2d ist seit Langem eine beliebte Wahl für die Spieleentwicklung durch Einzelpersonen oder kleine Teams
    • love2d ist ein C++-Programm mit eingebettetem Lua-Interpreter und damit ein gutes Ziel für Fennel
    • Da die meisten Linux-Distributionen love2d paketieren, lassen sich .love-Dateien leicht nativ ausführen
    • Dank emscripten lassen sich love2d-Spiele auch im Web bereitstellen
    • Deshalb nutzen die meisten Fennel-Spiele love2d
    • ./soko.bin und Gnomic Vengeance verwenden diesen Stack
    • Fennel+love2d ist ein vollständiges Beispiel für Lisp as icing
    • Fennel sitzt ganz oben im Stack, und es gibt praktisch keinen Weg, Lisp in die unteren Schichten auszubreiten
    • Bis heute ist es der erfolgreichste Lisp-Stack für Spieleentwicklung
  • S7 + raylib

    • In diesem Jam nutzten zwei Spiele den Stack S7+raylib: GhostHop und Life Predictor
    • Raylib ist eine C-Bibliothek mit Bindings für mehrere High-Level-Sprachen und ist in den letzten Jahren beliebter geworden
    • Da auch S7 in C implementiert und leicht einzubetten ist, lässt sich diese Kombination gut mit emscripten im Web bereitstellen
    • Auch S7+raylib ist ein Beispiel für Lisp as icing, und es wird interessant sein zu sehen, ob es in künftigen Jams populärer wird
  • Guile + Chickadee

    • Chickadee ist eine Spielebibliothek für Guile und implementiert fast alle interessanten Teile einschließlich Rendering in Scheme
    • In den jüngsten Jams wurden zwei Spiele mit Chickadee erstellt: Turbo Racer 3000 und Bloatrunner
    • Guile+Chickadee ist ein Beispiel für Lisp as cake
    • Chickadee umschließt einige C-Bibliotheken für Low-Level-Aufgaben wie das Laden von Bildern, Audio und Fonts, der eigene Code ist jedoch in reinem Scheme geschrieben
    • Auch Matrix- und Vektormathematik sind vollständig in Scheme implementiert
    • Es bietet einen Satz an Rendering-Primitiven, der mit love2d und raylib vergleichbar ist, ebenfalls in Scheme implementiert
    • Während andere Lisp-Spielebibliotheken häufig C-Bibliotheken wie nanosvg verwenden, hat Chickadee auch Fortschritte bei der Implementierung von Vektorgrafik-Rendering in Scheme gemacht
    • Chickadee hat die Grenzen des Guile-Compilers und der virtuellen Maschine ausgereizt, wodurch auch Guile verbessert wurde
    • Da die Entwicklung jedoch größtenteils von einer Person in begrenzter Freizeit vorangetrieben wurde, dauert es lange, Funktionsparität mit populäreren Spieleentwicklungsbibliotheken zu erreichen
    • Schon im aktuellen Zustand funktioniert es für diesen Zweck ziemlich gut
  • Hoot + HTML5 canvas

    • Hoot ist ein Scheme-to-WebAssembly-Compiler
    • Hoot kompiliert nicht die in C geschriebene Guile-VM per emscripten nach Wasm
    • Stattdessen implementiert es eine vollständige Wasm-Toolchain und ein neues Backend für den Guile-Compiler, das direkt Wasm ausgibt
    • Hoot ist vollständig in Scheme geschrieben
    • Anders als C-Programme, die mit emscripten kompiliert werden und linearen Speicher nach Wasm 1.0 anvisieren, zielt Hoot auf Wasm 2.0 mit GC-verwalteten Heap-Typen
    • Dank dieser Architektur liefern Hoot-Binaries keinen Garbage Collector mit
    • Dadurch sind sie deutlich kleiner als Lisp-Runtimes, die mit emscripten kompiliert wurden
    • Das Wasm-Binary eines Hoot-Spiels ist kleiner als 2 MiB, während das geprüfte love.wasm eines love2d-Spiels fast 6 MiB groß war
    • Hoot-Programme interagieren einfach mit JavaScript
      • Scheme-Objekte können leicht an JavaScript übergeben werden
      • JavaScript-Objekte können ebenfalls an Scheme übergeben werden
      • Denn die Objekte beider Seiten werden im selben Heap verwaltet
    • Browser-APIs lassen sich über Wasm-Imports ansprechen, sodass die integrierte HTML5-canvas-API für Spiele eine einfache 2D-Rendering-Option ist
    • In diesem Jam gab es 11 Spiele mit Hoot, darunter Cirkoban und Lambda Dungeon
    • Hoot+HTML5 canvas ist größtenteils ein dicker Kuchen mit etwas Zuckerguss
    • Hoot zum Booten zu bringen kostete ein Jahr und erhebliche finanzielle Mittel
    • Es nutzt nicht emscripten, sondern eine eigene Toolchain, und auch der Guile-Compiler wurde erweitert
    • Es gibt außerdem einen Wasm-Interpreter, der auf der Guile-VM läuft
    • Die canvas-API hingegen ist sehr High-Level
    • Ein kuchen-näherer Ansatz wäre, über Hoots JS-FFI WebGL oder WebGPU aufzurufen
    • Die künftigen Pläne gehen in Richtung WebGL/WebGPU; um das zu ermöglichen, sind Verbesserungen an Wasm GC nötig
    • Ein weiteres Ziel ist, Chickadee auf Hoot zu portieren, damit Chickadee-Spiele ähnlich wie love2d-Spiele leicht nativ und im Browser spielbar werden

Grenzen und Vorteile des Kuchen-Ansatzes

  • Auch der Kuchen-Ansatz hat klare Grenzen
  • Die heutige Umgebung ist nicht die Welt der Lisp-Maschinen, und selbst der höchste Lisp-Kuchen steht meist auf einem größeren Kuchen aus C
  • Moderne Lisp-Systeme erreichen an irgendeinem Punkt die darunterliegenden Schichten
    • Emacs steht auf einem C-Core
    • Die Guile-VM ist in C geschrieben
    • Hoot läuft auf großen C++-basierten JavaScript-Engines wie V8
    • Hoot-Spiele rendern derzeit mit HTML5 canvas statt mit WebGL/WebGPU
    • Für die Nutzung von OpenGL braucht man libGL
    • Chickadee verwendet guile-opengl, das libGL über C-FFI aufruft
    • Auch libpng, FreeType und andere existieren
  • Alles in Lisp neu zu schreiben, ist vor allem eine Ressourcenfrage
  • Trotzdem ist es ein kleiner Sieg, Teile des Stacks von Sprachen wie C zurückzuerobern
  • Die in Lisp geschriebenen Teile sind leichter zu hacken, und einige erlauben sogar Live-Hacking während der Programmausführung
  • Dank GC-verwalteter Runtimes erhält man in der Regel Speichersicherheit
  • Weniger FFI-Aufrufe verringern den Overhead beim Überschreiten der Lisp/C-Grenze und erhöhen auch die Sicherheit
  • Je größer der Lisp-Anteil im Stack wird, desto näher kommt er dem Kuchen statt dem Zuckerguss

Kuchen-Beispiele außerhalb von Spielen

  • Guix ist ein gutes Beispiel dafür, wie mächtig der Kuchen-Ansatz werden kann
  • Guix übernimmt das funktionale Packaging-Modell des Nix-Projekts und implementiert es neu, wobei die Nix-Sprache durch Guile ersetzt wird
  • Der Grund dafür sind Code-Staging, Code-Sharing und bessere Hackbarkeit
  • Guix nutzt außerdem ein in Guile geschriebenes Init-System statt systemd, und auch diese Entscheidung beruht auf denselben Gründen
  • Anfangs war Guix leicht als unnötige Neuerfindung des Rads zu kritisieren, doch zehn Jahre später ist das Beharren auf maximaler Lisp-Nutzung ein Kern des Projekterfolgs
  • Wer Guix-Idiome und etwas Guile lernt, erhält eine starke Fähigkeit, das Betriebssystem nach eigenen Vorstellungen zu konfigurieren
  • Guix kann als die Erfahrung gelten, die einer Lisp-Maschine auf moderner Hardware am nächsten kommt
  • Auf der Common-Lisp-Seite ist die Game Engine Trial ein Beispiel dafür, dass viele Teile in Common Lisp implementiert werden, statt C-Bibliotheken nur zu umschließen
  • Projekte wie Pre-Scheme wecken die Hoffnung, dass eines Tages auch Schichten unterhalb GC-verwalteter Runtimes in Lisp implementiert werden können
    • Pre-Scheme wurde in Scheme 48 entwickelt und erfolgreich genutzt
    • Dank eines NLnet grant ist eine moderne Wiederbelebung zu erwarten

Mehr vom Stack in Lisp bauen

  • Die Richtung geht eher zum Kuchen
  • Es braucht mehr Projekte, die die Grenzen dessen, was Lisp leisten kann, weiter verschieben
  • Das Interessanteste am Lisp Game Jam sind weniger die Spiele selbst als die kleinen Fortschritte dabei, ein Stück Kuchen vom alten, trockenen C zurückzuerobern
  • In der Guile-Spieleentwicklung soll das Chickadee-Projekt die Grenzen weiter verschieben
  • Das Fazit lautet nicht: in Rust neu schreiben, sondern in Lisp neu schreiben

1 Kommentare

 
GN⁺ 2024-06-03
Hacker-News-Kommentare
  • Es war umso erfreulicher, weil man heute kaum noch Texte sieht, die Software-Ansätze objektiv vergleichen
    Selbst wenn man nach solchen Texten sucht, kommen die Suchergebnisse heute oft nicht über SEO-Spam hinaus
    Janet scheint für Spiele gemacht zu sein, und es wirkt, als hätte es überraschend viele „Batteries included“-Elemente wie Webserver oder Grafik, daher war ich etwas überrascht, keine Spiele zu sehen, die Janet verwenden
    Im Bereich Lisp und Games ist es meiner Meinung nach eine Sprache, die man sich einmal ansehen sollte

  • Schön, dass s7 Aufmerksamkeit bekommt
    Ich habe es in Scheme for Max als Scheme verwendet, einer Open-Source-Erweiterung, die einen Scheme-Interpreter in die Computer-Musik-Umgebung Max/MSP einbettet; es liegt irgendwo zwischen Guile, Clojure und Common Lisp, ist dabei aber sehr klein und leicht einzubetten
    Gut ist auch, dass es unter einer deutlich großzügigeren BSD-Lizenz steht als Guile
    Wenn man Common-Lisp-Makros mit First-Class-Environments mag, stehen die Chancen gut, dass einem auch s7 gefällt
    Auch in WASM ließ es sich sehr einfach nutzen, und ich verwende es so in einem Musikbildungsprojekt
    Es war auch nicht schwer, generische Funktionen zu bauen, die aus Scheme heraus JS-Funktionen aufrufen und umgekehrt aus JS heraus Scheme aufrufen, sodass der gesamte Ablauf reibungslos war

    • Noch eine Stimme für s7
      Wir haben s7 und SQLite erfolgreich als Engine ohne Grafik in nativen Apps für iOS und Android eingebettet
      Es war sehr schnell, hatte ein gutes FFI, war stabil und klein, und wir profitierten stark von gemeinsam genutztem Code zwischen mobilen Apps, extrem schnellen Unit-Tests und einer sauberen Toolchain
      Am Ende sind wir auf Fennel umgestiegen, ein für Mobile praktischeres Lisp
      Während wir fast das einzige Team waren, das s7 auf Mobile nutzte, war Lua als Erweiterungssprache auf Mobile deutlich verbreiteter, und auch der Stand der r7rs-Scheme-Kompatibilität spielte eine Rolle
      Da wir für die Desktop-Entwicklung Guile nutzten und für die Auslieferung s7, stolperten wir oft über subtile Inkompatibilitäten, etwa die Auswertungsreihenfolge von Parametern
      Sowohl s7 als auch Fennel haben hervorragende Projekte und Communities
  • Ich empfehle, sich besonders das Spritely Institute anzusehen, einschließlich des Blogs
    Ich will nicht spoilern, was sie dort machen, aber es ist ein Thema und eine Institution, in die man sich gut vertiefen kann
    Allein mit dem Lesen des Blogs, verwandter Links und Projekte habe ich über 10 Stunden verbracht
    https://spritely.institute/archive/

    • Es wirkt wie ein Ort, der gute Leute anzieht und ziemlich coole Dinge tut
  • Die Zusammenfassung „Wir leben nicht in der Welt der Lisp-Maschinen, sondern in der Welt eines glorifizierten PDP-11“ ist eindrucksvoll
    Ich frage mich allerdings, ob es auch „Icing“ gibt, das man zusammen mit SDL verwenden kann

    • Traditionelle CPUs als glorifizierte PDP-11 zu bezeichnen, ist schwer haltbar
      In dem Sinne, dass es von-Neumann-Maschinen mit ungetaggtem Speicher sind, ähneln sie sich zwar, aber etwa ab Mitte der 80er, als 32-Bit-Mikroprozessoren allgemein wurden und sich die Lisp-Compilertechnik daran anpasste, begannen traditionelle CPUs, Lisp-Maschinen zu überholen
      Auch der Punkt „ungetaggter Speicher“ muss sich angesichts von Strömungen wie CHERI nicht unbedingt dauerhaft halten, und es gibt Spielraum für eine größere Rückkehr von LispM-artigen Architekturen
    • Ich mochte die PDP-11 wirklich sehr
      Ein paar Jahre lang hatte ich eine PDP-11/45 im Wohnzimmer stehen; später tauschte ich sie, um Platz zu sparen, gegen mehrere H-11 LSI-11/2 mit je zwei 8-Zoll-Diskettenlaufwerken
      Die PDP-11 war nicht nur so etwas wie das Protoplasma von Unix, sondern auch konzeptionell gut entworfen
      So wie wir Routinen bevorzugen, die „auf einen Bildschirm passen“, führte der kleine direkte Adressraum der PDP-11 zu nicht zu großen Modulen und förderte Modularität
      Ist das heute unzureichend? Natürlich, besonders bei Big Data ist es schmerzhaft
      Trotzdem gibt es konzeptionelle Gründe dafür, dass die PDP-11 erfolgreich war und bis heute Spuren hinterlassen hat
    • Ironischerweise bewegen wir uns jetzt zu C-Maschinen mit Hardware-Memory-Tagging
      Es gibt keinen anderen Weg, C zu reparieren, und es gibt zu viel Code, der nie neu geschrieben werden wird
    • Das Ende gefiel mir noch besser: „In Rust neu schreiben? Auf keinen Fall! Schreibt es in Lisp neu!
    • Lisp ist wegen der Speicherhierarchie nicht für moderne CPUs geeignet
      Lisp arbeitet hauptsächlich mit Listen, und Listen können Zeigern quer durch den Speicher folgen
      Bei früheren CPUs war das kein Problem, weil Speicherzugriffe im Allgemeinen ungefähr die gleiche Random-Access-Zeit hatten, aber moderne CPUs können Speicherzeigern nicht mit derselben Geschwindigkeit folgen und müssen für Performance Lokalitätsregeln einhalten
      Deshalb müssen Algorithmen, die etwas wie C- oder Fortran-Arrays verwenden, zwangsläufig immer schneller sein als Lisp-Listen-basierte Versionen
  • Die jüngsten Fortschritte bei Guile Scheme machen Hoffnung
    Anders als beim letzten Mal, als ich es mir angesehen habe, ist es von einer interpretierten Sprache zu einer Sprache mit einem ordentlichen Compiler geworden, und mit Hoot kann es nun auch nach WASM kompilieren
    Mit Clojure, uLisp und Common Lisp bin ich vertraut, aber Guile Scheme fühlt sich an, als hätte es viel von Common Lisps Ballast abgeworfen, und besonders wenn Guix und Shepherd sich durchsetzen, hätte ich gern ein kompiliertes Lisp zur Hand
    Ich frage mich, ob es neben Little Lisper und SICP gute Materialien gibt, um Guile Scheme effektiv zu lernen

  • Ich überlege, Guile für etwas zu verwenden, das ich demnächst bauen will, daher fand ich den Text interessant
    Auch die Arbeit auf der WASM-Seite scheint gelungen zu sein

  • Kürzlich habe ich mit Clojure einen Prototyp für einen 3D-Bosskampf gebaut: https://prototype-game.pages.dev

    • Großartig
      Früher habe ich jede Form von Webentwicklung gehasst, aber ClojureScript hat sie tatsächlich angenehm gemacht, und ich wünschte, es würde breiter genutzt
    • Auf Mobile funktioniert die Steuerung nicht
      Ich werde es später noch einmal versuchen, wenn ich am PC sitze
    • Cool
      Ich frage mich, welche Bibliotheken du verwendest
    • Mit Liebe zu dem, was du gebaut hast, gesagt: Es erinnert mich an Avatar - Legends of the Arena
      Das war eines der Spiele, die ich als Kind wirklich geliebt habe
      https://www.youtube.com/watch?v=dcJFldES9dg
  • Ich hatte zwar darum gebeten, es in die Tabelle aufzunehmen, aber vielleicht ist das inzwischen schon eine alte Nachricht.
    Referenz:
    https://lispy-gopher-show.itch.io/logos-lisp-legend/devlog/7...
    https://itch.io/post/10013482

  • Obwohl es https://ianthehenry.com/posts/janet-game/ gibt, fehlt Janet.
    Das Copyright-Jahr des Artikels ist mit 1899–1907 angegeben, aber schade ist, dass er nicht wirklich vintage wirkt.

    • Janet scheint zu fehlen.
      Für Scripting bin ich wieder zu Fennel zurückgekehrt, weil man viel mehr Lua-Bibliotheken direkt verwenden kann und es fast überall problemlos läuft, sogar in a-Shell auf dem iPad.
    • Ich habe Janet sehr ernsthaft geprüft, aber keinen direkten Weg zu meinem Ziel gesehen, Web-Apps und WebGL (ThreeJS) zu nutzen.
      In diesem Thread gab es nützliche Referenzen, und vielleicht versuche ich es später noch einmal: https://janet.zulipchat.com/#narrow/stream/409517-help/topic...
      Obwohl es bereits einen bewährten Produktionspfad gab, habe ich gerade noch rechtzeitig etwas Spielbares gebaut, wobei „spielbar“ schon recht großzügig formuliert ist.
  • Ich bin sehr neugierig, welche Art von Spiel mit Emacs Lisp gebaut wurde.
    Wenn man an Spieleprogrammierung denkt, ist das nicht gerade die naheliegendste Wahl.