- Die JavaScript-Syntax wird durch verschachtelte Klammern und Callbacks schnell komplex, und selbst für kleine UIs entsteht eine Aufblähung, weil viele Bibliotheken eingebunden werden
- WebAssembly eröffnet im Browser die Möglichkeit, andere Sprachen auszuführen, doch wie bei Pyodide sind die Kosten der asynchronen Anbindung an die JavaScript-Event-Loop hoch
- Browser-Ressourcen und WebAssembly-Speicher sind begrenzt, daher ist ein Ansatz nötig, der nicht versucht, JavaScript zu ersetzen, sondern mit ihm zu verhandeln
- LispE bündelt mehr als 450 Funktionen in einer einzigen 3,3-MB-WASM-Binärdatei und bietet zugleich Strings, Berechnungen, Matrizen und reguläre Ausdrücke
- Mit
evaljs und asyncjs lassen sich JavaScript-Funktionen und das DOM nutzen, während die Code-Aufblähung durch eine einzelne auditierbare Binärdatei statt vieler externer Bibliotheken reduziert wird
JavaScript-Aufblähung und Browser-Beschränkungen
- Die JavaScript-Syntax wird leicht komplex, weil runde, geschweifte und eckige Klammern ineinander verschachtelt sind und in der richtigen Reihenfolge geschlossen werden müssen
- Es wird ein Beispiel gezeigt, in dem innerhalb eines
forEach-Callbacks Werte in mehrere Objekte geschrieben und ein bedingter Cache aktualisiert wird
newNames.forEach((name, i) => {
allAgentContents[name] = contents[i];
agentModes[name] = modes[i];
if (compiled[i]) agentCompiledCache[name] = compiled[i];
agentViewingCompiled[name] = viewing[i];
});
- Für Verarbeitungen, die in grundlegenden Programmiersprachen oft enthalten sind, lädt man in JavaScript viele Bibliotheken herunter
- Auch in C oder C++ braucht man für die Arbeit
include, aber bei JavaScript-Bibliotheken ist oft schwer zu erkennen, wer sie implementiert hat und wer sie wartet
- Es gibt sogar Seiten, die für die Anzeige von hello in einem kleinen Fenster scheinbar „die halbe Internetwelt“ laden
- Das Internet hängt von JavaScript ab, und auch wenn TypeScript manches verdeckt, bleibt es beim Öffnen einer Seite im Browser der Pförtner, an dem alles vorbeimuss
- Es gab den Traum, dass der Browser zum ultimativen Betriebssystem wird und Windows oder Mac OS überflüssig macht, und JavaScript wurde als Sprache gewählt, um diesen Traum umzusetzen
Die Beziehung zwischen WebAssembly und JavaScript
- WebAssembly ist eher mit einer virtuellen Maschine mit eigener Maschinensprache vergleichbar und eröffnet die Möglichkeit, im Browser auf andere Weise zu programmieren
- Pyodide ist eine beeindruckende Ingenieursleistung, um Python im Browser auszuführen, zeigt aber auch die Kosten der Bewegung innerhalb der JavaScript-Welt
- Zwei asynchrone Welten müssen miteinander sprechen: Pythons
asyncio und die JavaScript-Event-Loop
- Die Brücke zwischen beiden Welten ist fragil, und man muss sich merken, zu welcher Welt jedes
await gehört
- Browser-Ressourcen sind begrenzt, daher ist eine Denkweise nötig, die wie in den frühen Tagen der Informatik jede Anweisung und Struktur bis auf Bit-Ebene betrachtet
- Als Maßstab wird genannt, dass das Programmieren 1981 auf einem Computer begann, auf dem exakt 15772 bytes frei waren
- Moderner Browser-Speicher ist zwar nicht so begrenzt wie auf diesem ersten Computer, aber WebAssembly kann Speicher nicht wie ein gewöhnliches Programm frei besitzen, sondern muss ihn zunächst auf eingeschränkte Weise zugewiesen bekommen
- Die zentrale Haltung besteht darin, nicht gegen JavaScript zu kämpfen, sondern mit ihm zu verhandeln
Die von LispE vorgeschlagene Alternative
- LispE stellt in einer einzelnen Binärdatei mehr als 450 Funktionen bereit
- Die Größe der WASM-Binärdatei beträgt 3,3 MB
- Funktionen für Strings, Berechnungen, Matrizen und reguläre Ausdrücke sind an einem Ort gebündelt
- Die WASM-Binärdatei befindet sich unter binaries/wasm
- Obwohl es ein kleiner Interpreter ist, kann er als Rückgabewerte Strings,
Float64Array, Ganzzahlen, Fließkommazahlen und String-Arrays verarbeiten
- LispE basiert auf Lisp und besitzt daher eine Struktur, in der der AST lebendig bleibt
- Wenn die Lisp-Syntax nicht passt, kann eine transpiliertaugliche Syntax für eine Sprache verwendet werden, die sich kaum von Python oder Basic unterscheidet
- Durch Anpassung von grammar lässt sich eine Sprache im gewünschten Stil erstellen
- Das ist sogar auf Griechisch möglich; ein griechisches Beispiel existiert bereits
- LispE arbeitet nicht gegen JavaScript, sondern kooperiert damit, indem es JavaScript-Funktionen und DOM-Funktionen nutzt
JavaScript-Aufrufe: evaljs und asyncjs
- Um JavaScript-Code innerhalb von LispE auszuführen, können
evaljs und asyncjs verwendet werden
evaljs führt JavaScript-Code aus und liefert einen Wert zurück
asyncjs verbindet benutzerdefinierte JavaScript-Funktionen auf der Seite mit asynchronen Callbacks
(setq a (evaljs "10 + 20 + 30")) ; execute some JS code
; call_llm is a user-defined JS function in the page
(asyncjs `call_llm("Implement a piece of code in Python to sort strings");` 'mycallback)
(defun mycallback(theresult) ...)
asyncjs arbeitet als Promise, das zurückkehrt, sobald die Aufgabe abgeschlossen ist
Code-Aufblähung reduzieren
- Ein Zitat von Wirth aus dem Jahr 1995 führt zu der Schlussfolgerung, dass schnellere Computer und größere Modelle das Problem nicht lösen und Code schlank gehalten werden muss
- LispE stellt die im Browser verfügbaren 450 Funktionen als einzelne auditierbare Binärdatei bereit
- Würde man jede Funktion separat umsetzen, müsste man Bibliotheken wie
mathjs für numerische Berechnungen, lodash für Collections, voca für String-Manipulation und simple-statistics für statistische Verteilungen laden
- Auf diese Weise kann der Code auf Hunderte MB anwachsen, mit unterschiedlichen Autoren, Bugs und Wartungszyklen
- LispE bietet diese Funktionen in einer einzigen gepflegten Codebasis an und ermöglicht als Open Source eine vollständige Codeprüfung
- Es wird davon ausgegangen, dass der Garbage Collector die Performance des Codes nicht im ungünstigsten Moment einbrechen lässt
- Mit JavaScript kommuniziert es transparent über einfache API-Aufrufe und kann vordefinierte Strukturen zurückgeben
// floats here is a Float64Array
const floats = callEvalLispEToFloats(0, `(normal_distribution 100)`);
2 Kommentare
Der Artikel wirkte irgendwie etwas aus dem Nichts, deshalb habe ich an der Quelle gezweifelt, aber dass es Naver war, wow.
Die Reaktionen sind aber erwartungsgemäß nicht gut ... Ehrlich gesagt ist es schon Over-Engineering, das nicht leicht nachzuvollziehen ist: JavaScript wird überzogen dargestellt, und dann will man sogar ein 3,3 MB großes WASM hochladen, um Lisp zu verwenden, haha.
Ein Entwickler des Projekts namens Claudius hat in einem Kommentar erklärt, warum es über einen Naver-Account gepostet wurde: Er arbeitet bei Naver Labs Europe, und Naver habe es als Open-Source-Projekt genehmigt, sodass es veröffentlicht wurde.
Mit Naver selbst scheint es also nicht besonders viel zu tun zu haben; es wirkt eher so, als wären das einfach Leute, die Lisp wirklich lieben ...
Meinungen auf Lobste.rs
JavaScript entblähen zu wollen, indem man einen intransparenten 3,3-MB-WASM-Klotz hinzufügt und die App dann in Lisp schreiben soll, wirkt fragwürdig.
Nur mit reinem JavaScript und null zusätzlichen Abhängigkeiten kann man schon ziemlich viel bauen, bevor man überhaupt ein Zehntel dieser Größe erreicht.
Ich teile diesen Anwendungsfall zwar nicht, aber es ist ein interessantes Lisp-Leidenschaftsprojekt mit einer heute seltenen eigentümlichen Note, und die gesamte Dokumentation hat diesen unverkennbar von Menschen geschriebenen Stil mit eigenwilligen Formulierungen.
Vorschläge für Dinge, die neu in die Standardbibliothek aufgenommen werden könnten, sind immer willkommen.
Zu den jüngeren Ergänzungen gehören Mengenvereinigung/-schnitt,
sum,base64usw.Wünsche nach Analysisfunktionen hört man dagegen kaum, und bei Strings werden vor allem
.reverseoder.titleCaseimmer wieder gefordert.Für
.reversegibt es abseits von Spielzeugproblemen nicht viele überzeugende Anwendungsfälle, und.titleCaseist grundsätzlich möglich, benötigt aber Internationalisierungsdaten, daher läuft die Diskussion noch.Außerdem gibt es beides in dieser Bibliothek nicht.
Manche haben das Gefühl, dass sich ein Vorschlag nicht lohnt, weil es am Ende drei Jahre bis zur tatsächlichen Übernahme dauert, und denken zugleich, dass ein
utils/-Ordner zwar unschön ist, sich aber sofort umsetzen lässt.Mein Punkt ist weniger, dass JavaScript zu wenige Funktionen hat, sondern eher das Distributionsmodell.
Selbst mit einer randvollen Standardbibliothek schleppt eine durchschnittliche App weiterhin transitive npm-Abhängigkeiten im Megabyte-Bereich mit.
LispE-as-WASM ist mit 3,3 MB, rund 450 dokumentierten Funktionen, einem C++-Kern und als Open Source auf GitHub ein Experiment dafür, wie eine auditierbare Runtime aussehen könnte.
Ich habe darauf tatsächlich ein Agent-Harness gebaut und führe LispE-Regeln direkt im Browser on the fly aus.
Die JavaScript-Event-Loop mag ich sehr.
Wenn jemand von aufgeblähter Syntax spricht, werde ich immer skeptisch.
Eine minimale Syntax kann viel schwerer zu lesen sein als eine, die zur visuellen Unterscheidung unterschiedliche Zeichen verwendet.
Was LispE als Alternative anbietet, sieht dann so aus:
... !?
https://github.com/naver/lispe/wiki/5.3-A-la-APL
Der Einstieg in dieses Projekt ist etwas seltsam, und https://github.com/naver/lispe/wiki/1.-Introduction scheint der bessere Startpunkt zu sein.
Es ist ein natives Lisp-Projekt, das auch auf WASM zielt, und hat einige interessante Aspekte, die Fans von Programmiersprachen gefallen dürften.
Mein erster Eindruck war ein ziemlich bemühtes JavaScript-Beispiel.
Ein großer Teil des JavaScript-Hasses scheint aus früher Browser-Inkompatibilität, schmerzhaften DOM-APIs und Fallstricken in der Syntax zu stammen, und all das beeinflusst meinen Alltag heute praktisch nicht mehr.
Modernes JavaScript, besonders mit TypeScript und vernünftigen Lint-Regeln, ist als Sprache völlig in Ordnung.
Probleme wie CJS vs. ESM, Supply-Chain-Risiken oder Turbulenzen im Ökosystem bleiben zwar, aber die liegen größtenteils außerhalb der Sprache selbst.
Ist also genau die Erfahrung großartig, die so lästig ist, dass Leute eigens Editor-Erweiterungen gebaut haben, nur um Klammern auszubalancieren?
Wenn man diesen Stil des Programmierens möchte, also geringe Syntaxdichte und funktionale Arbeitsweise, dann ist es vermutlich sinnvoller, die Richtung umzukehren und zu einer concatenativen Sprache zu greifen.
Ich frage mich, wie das kompilierte Ergebnis eines Lisp-Interpreters überhaupt auf 3,3 MB kommen kann.
Soll das weniger aufgebläht sein?
Selbst dann würde ich zustimmen, dass das Mitverteilen einer großen Standardbibliothek einschließlich ungenutztem Code ziemlich weit von „entblähen“ entfernt ist.
Schock! Horror!
Wie erstaunlich, dass schließende Klammern in der richtigen Reihenfolge geschlossen werden müssen.
Selbst Lisp-Fans, die die Ausführlichkeit von Sprachen wie JavaScript verabscheuen, wenden sich bei Sprachen aus der APL-Familie oft ab und beginnen zu argumentieren, dass Klarheit und Lesbarkeit viel wichtiger seien als maximale Kürze.
Der Autor von LispE scheint eine gewisse Zuneigung zu grundlegenden APL-Operationen zu haben, aber es ist schwer nachzuvollziehen, warum man nach der Begegnung mit einer so kompakten Notation Conways Life lieber als längere, lockerere, tief verschachtelte s-Expression-Version schreibt als in APL, J, K oder sogar Q.