3 Punkte von GN⁺ 2023-07-28 | 1 Kommentare | Auf WhatsApp teilen
  • Ausgehend von der Frage nach einer Sprache, die auch in Zukunft noch nutzbar ist, ging es bei dieser Wahl weniger um Praktikabilität als darum, einen alten, aber modernen Entwicklungs-Workflow selbst auszuprobieren
  • Janet ist eine kleine Skriptsprache aus der Lisp/Clojure-Familie für einen ähnlichen Einsatzbereich wie Lua und weckte Interesse wegen ihrer Standardbibliothek und makrobasierten Metaprogrammierung
  • Die bei Janet attraktiv wirkenden Eigenschaften Portabilität, Einbettbarkeit und Parsing Expression Grammars passten nur begrenzt zu den aktuellen Anforderungen, und bestehende Optionen wie CHICKEN Scheme, CLISP, SBCL und Packrat wirkten ebenfalls völlig ausreichend
  • Die REPL-gesteuerte Entwicklung in Common Lisp zeichnet sich vor allem dadurch aus, dass man bei Laufzeitfehlern Stack, Variablen und Live-Daten untersuchen und verändern und danach die unterbrochene Ausführung fortsetzen kann
  • Zwar kostet es etwas, eine neue Sprache und Standardbibliothek zu lernen, doch der Autor hält es für lohnend, diesen Workflow in einer Umgebung zu erleben, in der er von Anfang an Teil des Designs war

Warum das Interesse von Janet zu Common Lisp wechselte

  • Aus dem Wunsch heraus, Code zu schreiben, den man auch in Zukunft noch verwenden kann, wurde über future-proof programming languages nachgedacht, doch beim Bauen und Veröffentlichen von Dingen überwog meist ein pragmatischer Ansatz: beliebt, portabel und bequem
  • Gleichzeitig wurden zum Spaß und zum Experimentieren auch andere Programmiersprachen und Werkzeuge betrachtet
    • Interessante Sprachen mit kurzen Namen wie Nim und Zig wurden beobachtet
    • Solche Experimente führen manchmal dazu, neue Arten des Programmierens oder Werkzeuge zu entdecken, die später unverzichtbar werden
  • Vor Kurzem entstand durch das kostenlose Buch Janet for Mortals Interesse an Janet
    • Janet ist eine relativ kleine, von Lisp/Clojure inspirierte Skriptsprache für eine ähnliche Nische wie Lua
    • Sie bietet eine echte Standardbibliothek, Lisp-artige Metaprogrammierung und Compile-Time-Ausführung per Makros
  • Am meisten reizten an Janet neben dem allgemeinen Lisp-Charakter Portabilität, Einbettbarkeit und Parsing Expression Grammars
    • Da derzeit keine Sprache eingebettet werden muss, war die Einbettbarkeit kein entscheidender Grund
    • Bei der Portabilität wirkten auch CHICKEN Scheme, CLISP und Steel Bank Common Lisp überzeugend
    • Für Parsing schien Packrat eine vernünftige Wahl zu sein
  • Am Ende blieben also immer weniger Gründe, Janet statt eines bestehenden Lisp zu wählen

Der REPL-gesteuerte Workflow, der mich Common Lisp lernen ließ

  • Ein Artikel über den beinahe einzigartigen REPL-gesteuerten Workflow von Common Lisp wurde zur Lernmotivation
  • Die Beispielsituation ist, dass die Funktion foo die noch nicht definierte Funktion bar aufruft und während der Ausführung von foo ein Fehler auftritt
    • In älteren Lisp- oder Smalltalk-Umgebungen führt die Unterbrechung von foo zu einer Breakloop
    • Die Breakloop ist eine vollständige REPL innerhalb der dynamischen Umgebung der unterbrochenen Funktion
    • Man kann sich durch den unterbrochenen Call Stack bewegen und die in jedem Stack-Frame sichtbaren Variablen prüfen
    • Die Live-Daten des laufenden Programms lassen sich untersuchen und verändern
    • Wenn ein falscher Variablen- oder Feldwert die Ursache ist, kann man den Wert ändern und danach die unterbrochene Funktion fortsetzen
    • Man kann die fehlende Funktion bar direkt in der REPL definieren und foo wieder aufnehmen, um ein plausibles Ergebnis zu erhalten
  • Zwar wurden schon viele „edit and continue“-Funktionen gesehen, aber dass Common Lisp mit genau dieser Art von Workflow im Blick entwickelt wurde, wirkte neu
  • Die bisherige Debugging-Erfahrung verlief von printf debugging hin zur Nutzung echter Debugger
    • printf debugging bedeutet, vorübergehend Logging-Code einzufügen, neu zu kompilieren und dann die Ausführung zu prüfen
    • In Situationen, in denen sich Programme nicht verändern oder neu starten ließen, musste man den Umgang mit Debuggern lernen
    • Ein echter Debugger ist das richtige Werkzeug, doch die Einrichtung der Debugging-Umgebung kann schmerzhaft oder manchmal unmöglich sein
  • Common Lisp wirkte wie eine Umgebung, die Debugging noch einen Schritt weiter treibt
    • Es gab bereits Erfahrungen damit, im Debugger Speicher zu verändern, um potenzielle Fixes zu testen
    • Besonders attraktiv erschien aber die Möglichkeit, Code direkt im laufenden Prozess umzuschreiben und zu patchen
  • Das Ziel beim Lernen von Common Lisp ist, Programme interaktiv aufzubauen und selbst herauszufinden, ob diese Arbeitsweise Spaß macht
  • Es ist nicht sicher, ob das Lernen einer neuen Sprache samt Standardbibliothek die beste Wahl ist, um einen neuen Workflow zu erkunden, doch eine klar bessere Alternative ist ebenfalls nicht erkennbar
    • Auch in anderen Sprachen könnte es ähnliche REPL-Editor-Integrationen geben
    • Solche Funktionen wurden dort aber womöglich nachträglich ergänzt statt von Anfang an eingeplant und könnten daher fehleranfälliger sein
    • Selbst wenn es nicht überzeugt, bleibt immerhin, es unter den bestmöglichen Bedingungen mit den Standardwerkzeugen ausprobiert zu haben
  • Materialien, die beim Lernen von Common Lisp geholfen haben, sind in einem separaten Beitrag Common Lisp resources zusammengestellt

1 Kommentare

 
GN⁺ 2023-07-28
Meinungen auf Hacker News
  • Hätte nicht erwartet, meinen Artikel hier zu sehen. Irgendwann möchte ich auch einen Folgebeitrag schreiben, aber im Moment bin ich noch Anfänger.
    Was mir an Common Lisp bisher gefallen hat, ist, dass das Condition System sauber ist und man mit Restarts auch weit entfernten Code leicht steuern kann. REPL-getriebene Entwicklung ist ebenfalls praktisch, wenn man nicht genau weiß, was passieren wird – etwa beim Parsen einer unbekannten Datenquelle –, weil man den Code anpassen und weitermachen kann, ohne den Kontext zu verlieren.
    Es gibt viele Implementierungen, und die Interoperabilität ist gut; in einem Fall konnte ich für Geschwindigkeit SBCL und zur Reduzierung des Speicherverbrauchs CLISP austauschbar verwenden. Einer der Gründe, warum ich beim Lernen von Lisp eher zu CL als zu Scheme tendierte, war ebenfalls, dass es mehrere kompatible Implementierungen gibt.
    Obwohl ich Emacs-Anfänger bin, war die Common-Lisp-Integration hervorragend, und sie läuft sogar auf dem sehr langsamen Netbook gut, das ich für die Entwicklung nutze. In Zeiten schneller Computer, VS Code und Language Server ist das kein ganz so großer Vorteil mehr, aber es hat definitiv ein Retrofuture-Gefühl.
    Es gibt auch Dinge, die mir weniger gefallen. Der beliebteste Package Manager, QuickLisp, ist in Ordnung, erreicht aber nicht das Funktionsniveau, das man aus modernen Sprachökosystemen gewohnt ist. Die Sprache selbst ist in der Zeit eingefroren, sodass man für vieles – Threads, Synchronisation, Kommandozeilenargumente usw. – Interop-Bibliotheken braucht. Und ich fände es wirklich großartig, wenn SBCL vollständig statische Builds unterstützen würde, damit man Binaries auch für Linux-Distributionen ohne glibc ausliefern kann.
    • Es gibt auch einige Nachteile. Als ich zuletzt nachgesehen habe, holte QuickLisp Pakete nur per unverschlüsseltem HTTP, ohne Verschlüsselung und ohne Prüfmechanismus, der Manipulationen während der Übertragung erkennen könnte.
      Wenn eine so wichtige Aufgabe wie das Abrufen von Quellcode keine Verschlüsselung oder Authentifizierung unterstützt, ist QL meiner Meinung nach für sicherheitsbewusste Menschen kaum eine Option.
      Außerdem ist es ein Problem, dass SBCL auf SourceForge gehostet wird. SourceForge hat in der Vergangenheit Malware in Download-Archive eingeschleust, daher sehe ich auch das als Sicherheitsproblem. Ich sehe heute keinen triftigen Grund mehr, SourceForge weiter zu nutzen, und verstehe nicht wirklich, warum ein wichtiges Projekt immer noch dort liegt.
      Ich mag Lisp und besonders Common Lisp wirklich, aber solche Dinge haben mich davon weggebracht, und es scheint auch nicht viel Interesse daran zu geben, sie zu beheben.
    • Wenn du statische Builds brauchst und mit einem etwas älteren SBCL leben kannst, könnte daewoks Arbeit, SBCL in einer musl-Umgebung zu bauen und zu linken, die gesuchte Lösung sein. Ich habe versucht, die Patches auf eine neuere Version zu übertragen, aber wegen Upstream-Änderungen blieb ein Segmentation Fault übrig.
      https://www.timmons.dev/posts/static-executables-with-sbcl.h...
      https://www.timmons.dev/posts/static-executables-with-sbcl-v...
    • Ich gebe dir ein cons.
      https://cons.io
      Gerbil/Gambit Scheme kann als Alternative zu CL vollständig statische Binaries erzeugen.
    • Für den Fall, dass man mit einer guten Sammlung von Bibliotheken starten möchte (JSON, CSV, Datenbanken, HTTP-Client, Kommandozeilenargumente, Spracherweiterungen usw.), arbeite ich an CIEL: https://github.com/ciel-lang/CIEL/
      Es kann als normale Quicklisp-Bibliothek, als sofort startendes Core Image oder als Binary verwendet werden. Skripte lassen sich ebenfalls fast sofort ausführen, insofern ähnelt es Babashka ein wenig. Ich feile noch an den Details, und v1.0 ist es noch nicht.
      Im Blog hätte „Laufzeitfehler behandeln, indem man kaputten Code an Ort und Stelle repariert, ohne neu zu starten“ hier gezeigt werden sollen: https://www.youtube.com/watch?v=jBBS4FeY7XM
      Wenn bei einer langen, schweren Berechnung im letzten Schritt ein Fehler auftritt, springt man statt eines kompletten Neustarts in den interaktiven Debugger, geht zur fehlerhaften Zeile, kompiliert die korrigierte Funktion, kehrt zum Debugger zurück und nimmt die Ausführung an einem Punkt im Stack Frame wieder auf. Danach kann man sehen, wie das Programm durchläuft.
  • Bei der Arbeit verwende ich Clojure, aber immer wenn ich debugge oder schnellen Code brauche, vermisse ich fast alles an Common Lisp. Es ist extrem nützlich, in verschachtelten Fehlern auf beliebige Teile des Stacks klicken und die lexikalischen Bindings ansehen zu können; ein Objekt anzuklicken und es dann mit M- in die REPL zu kopieren, fühlt sich für mich deutlich besser an als das, was Clojure mit tap> bietet. Selbst mit Tools wie Portal wirkt tap> für mich eher wie aufgehübschtes Pretty-Printing.
    Auch bei der Performance kann Common Lisp statische Typangaben nutzen, und SBCL kann daraus sehr effizienten Code erzeugen. Es hilft, DISASSEMBLE auf selbst geschriebenen Code laufen zu lassen, um zu sehen, was tatsächlich ausgegeben wird, und ihn entsprechend zu optimieren. Besonders Pakete wie SB-SIMD und Loopus sind für numerische Anwendungen wirklich sehr hilfreich.
    • Das fasst gut zusammen, was mich beim Schreiben meiner Masterarbeit in Clojure gestört hat. Nicht, dass ich die Wahl an sich bereue. Clojure passt mit seinem Gefühl von „alles ist eine Key-Value-Map“ und dem Ansatz „wenn der Key :quack true ist, behandle es wie eine Ente“ gut zu Entity-Component-System-artigem Game Design.
      Aber die Entwicklungsweise von Common Lisp und überhaupt das Condition System habe ich im letzten Jahr wirklich vermisst. Obwohl ich gar kein besonders versierter CL-Hacker bin, ging es mir schnell in Fleisch und Blut über. Ich wünschte, CLOS und die primitiven Datentypen von CL würden besser zusammenpassen, als es auf den ersten Blick wirkt.
    • In Clojure lohnt sich ein Blick auf flowstorm. Man kann innerhalb von Funktionen vorwärts und rückwärts schrittweise ausführen und Maps zusammen mit diesen Funktionen an die REPL schicken.
    • Ich denke, keine der beiden Sprachen stellt das Senden von Forms an die REPL als Sprachfeature selbst bereit; das ist eine Funktion der Tools.
      In Cider geht das definitiv einfach, und die wichtigsten Tools anderer Editoren dürften das ähnlich gut hinbekommen.
    • Auch in Clojure kann man in gewissem Umfang etwas Ähnliches wie DISASSEMBLE machen.

Es gibt Hilfsprojekte wie https://github.com/Bronsa/tools.decompiler, und auch JitWatch von OpenJDK (https://github.com/AdoptOpenJDK/jitwatch). Für andere JVMs gibt es ähnliche Tools.
Nicht so intuitiv wie Lisp, aber es ist machbar.

  • Steel Bank Common Lisp ist das Arbeitspferd, das es mir ermöglicht hat, profitable Softwarefirmen aufzubauen. Ohne es wäre ich vermutlich nicht so produktiv gewesen. Der REPL-getriebene Workflow ist großartig, Lisp-Images sind sehr robust, und die Performance ist hoch.
    • Kannst du für Interessierte teilen, welche Firmen das sind?
    • Sieht cool aus, aber im Moment bin ich zu faul, wieder zu Emacs zurückzukehren. Für Python- und JavaScript-Entwicklung nutze ich hauptsächlich VSCode mit nahezu Standardeinstellungen. Ich bin CTO, daher programmiere ich nicht Vollzeit.
    • Ich denke, die Zeiten, in denen der Tech-Stack auch nur ein wenig wichtig war, sind größtenteils vorbei.
      Es ist schön, dass ihr etwas Esoterisches geschaffen habt, das attraktiv und gut funktioniert, aber jetzt wird es niemand mehr anfassen wollen außer Leuten, deren Jahresgehalt man mit der Anzahl der Nullen ausdrücken kann.
    • Es heißt, der REPL-getriebene Workflow sei erstaunlich, aber mir scheint, manchen ist nicht klar, dass heutzutage praktisch jede VM-/Interpreter-Sprache eine REPL hat.
      https://www.digitalocean.com/community/tutorials/java-repl-j...
      https://github.com/waf/CSharpRepl
      https://pub.dev/packages/interactive
      Ganz zu schweigen von Ruby, Python, PHP und Lua; selbst C++ hat eine, wenn auch holprige, REPL: https://github.com/root-project/cling
      Jedes Mal, wenn ich sage, dass die REPL bei Lisp kein Alleinstellungsmerkmal mehr ist, werde ich heruntergevotet.
  • Wenn du dich für 3D-Grafik interessierst, gibt es eine Common-Lisp-Codebasis, die einen Blick wert ist. Ich habe versucht, sie einfach und leicht verständlich zu halten.
    https://github.com/kaveh808/kons-9
    • Es lohnt sich, auf YouTube nach Kavehs CL-Tutorialvideos zu suchen. Sie sind wirklich gut gemacht.
  • Als Clojure-Entwickler finde ich den break loop und den REPL-getriebenen Workflow spannend, und ich denke, sie würden Clojure eindeutig guttun. Es könnte sich stärker wie Frontend-Coding mit JS/TypeScript anfühlen, bei dem man die hervorragenden Debugging-Tools des Browsers nutzt. Leider wirken der aktuelle Stand der Tools und die Unterstützung durch die Community im Clojure-Ökosystem ziemlich schwach.
    • Auch Clojure kann einen echten REPL-Flow bis zu einem gewissen Grad nachahmen, wenn Aufrufe tief in den Code-Schichten bei jeder Anfrage stattfinden, etwa bei einem Webserver. Während der Server weiterläuft, kann man mehrere Funktionen neu schreiben und neu laden und dann im Browser erneut anfragen.
      Allerdings verschmutzen solche Neudefinitionen und Überschreibungen am Ende den Namespace, und irgendwann geht etwas kaputt, sodass man den Server neu laden muss.
  • Ich mag CL und vermisse es wirklich, wenn ich andere Dinge mache. Verglichen mit meiner Lisp-Erfahrung vor Jahrzehnten sind viele Dinge in „modernen“ Sprachen so schmerzhaft, dass es nicht einmal lustig ist.
    Es hat auch viele Nachteile, aber sie sind nicht schlimmer als die rein technischen Nachteile fast jeder anderen Sprache und Entwicklungsumgebung. Python und JS sind weiter verbreitet und haben mehr Bibliotheken, aber die Entwicklungserfahrung empfinde ich als deutlich schlechter. Ich habe über viele Jahre viel mit C#, TS, Py, Hs und noch obskureren Sprachen gearbeitet, aber jedes Mal, wenn ich in diese Sprachen abtauche oder mich ernsthaft über offenkundig schlechte Dinge ärgere, kehre ich zu CL (SBCL + Emacs + Slime) zurück. Dann bin ich beruhigt und überzeugt, dass es auf der Welt doch noch etwas Gutes gibt.
    Derzeit sind wir dabei, Finanzierung für ein Produkt einzuwerben, das wir törichterweise in TypeScript gebootstrappt haben; die Release-Version werden wir in CL neu bauen. Das bedeutet, dass wir in den kommenden 3–5 Jahren bis zum Release mit CL und innerhalb von CL arbeiten werden, und ich freue mich auf die spannenden Dinge wie DSL-Implementierung, Code-Generierung, Makroarbeit und den Bau eines statischen Typ-Analysators.
    • Ein Lisp ohne cons kann es nicht geben.
      Was vermisst du aus Lisp, wenn du mit C# arbeitest?
    • Warum ist TypeScript für dieses Produkt ungeeignet?
  • Im Blog und in den Kommentaren des Autors hier geht es viel ums „Coden“, aber man sieht kaum, welche Art von Software gebaut wird und auf welche Use Cases sie abzielt.
    Meine zugespitzte These ist: Funktionale Programmierung hat sich nicht breit durchgesetzt, weil sie zwar durchaus gut zum Schreiben von Programmen ist, die meiste heutige Software aber nicht „ein Programm ist, das auf meinem PC/Server über die Kommandozeile gestartet wird und bis zum Ende durchläuft“, sondern „ein Programm, das startet, auf Benutzereingaben reagiert und endet, wenn der Benutzer es schließt“ oder „ein Programm, das startet, dann auf Netzwerk- oder andere automatische I/O reagiert und stoppt, wenn andere Software ihm sagt, dass es stoppen soll“.
    Solche Aufgaben sind im rein funktionalen Stil deutlich schwieriger, oder zumindest war das bei den meinungsstarken funktionalen Implementierungen so, die ich ausprobiert habe. Denn man evaluiert nicht mehr einfach einen Ausdruck, sondern initialisiert Zustand, reagiert auf I/O, aktualisiert Zustand oder führt zusätzliche I/O aus und nutzt womöglich sogar Parallelisierung für Monitoring, Empfang, weitere I/O und Zustandsaktualisierung.
    Natürlich ist es nicht unmöglich, so etwas in Lisp zu machen, aber nach einigen Semestern funktionaler Programmierung im Studium und professioneller Nutzung funktionaler Features in C++/Scala zur Lösung solcher Probleme empfand ich es als ziemlich schwierig, funktionale Programmierung in solchen Anwendungen gut zum Laufen zu bringen; diese mangelnde Passung hat mich zögern lassen, tiefer in funktionale Programmierung einzusteigen.
    • Guter Punkt. Im Moment ist das alles nur Hobby. Mit Common Lisp habe ich einen gewöhnlichen Battlesnake-Client gebaut und ein Multiplayer-Wortmischspiel, das seit gestern eingestellt ist. Keines von beiden hat durch Lisp einen großen Vorteil gewonnen, aber ich habe dabei viel gelernt, und darum ging es.

Unabhängig von Common Lisp musste ich schon oft Code schreiben, der Code erzeugt. In diesem Bereich dürfte Lisp glänzen, aber ich hatte noch keine Gelegenheit, es auszuprobieren. Beispiele aus jüngeren Projekten, die ich vor Common Lisp gemacht habe, sind die Generierung von Code in einem statischen Site-Generator zur Validierung eines bestimmten JSON Schema sowie die Erzeugung von JSX aus Markdown für Story-Inhalte in einem Programmierspiel
Ganz zu schweigen von den zahllosen C-Makros, die ich im Laufe der Zeit geschrieben habe

  • Common Lisp enthält mit CLOS bis heute eines der fortschrittlichsten objektorientierten Systeme. Die meisten Lisps sind keine funktionalen Sprachen wie Haskell
  • Elixir nutzt funktionale Programmierung und ist sehr stark, wenn man Webentwicklung oder fehlertolerante Systeme braucht
    Man muss auch nicht alle guten Features anderer Stile wegwerfen. Teile der funktionalen Programmierung werden auch in gewöhnlichen Sprachen immer verbreiteter. Rust nutzt zum Beispiel in vielen Fällen funktionale Patterns
    Und Lisp kann man, wenn man möchte, auch objektorientiert oder imperativ schreiben. Es ist nicht Haskell
  • Es hat eine Weile gedauert, Monaden und die IO-Monade zu verstehen, und noch länger, zu lernen, wie man sie sicher komponiert und die Ausführungsreihenfolge steuert. Aber inzwischen kann ich typsichere Anwendungen schreiben und mache auch weniger Bugs, wenn ich in nicht-funktionalen Sprachen arbeite. Die Sprache, für die ich bezahlt werde, ist Java
    Lisp sehe ich als Ausgangspunkt, Haskell als das Eigentliche. Ich empfehle, diesen Stil zu lernen, auch wenn man keinen echten Produktionscode damit schreibt
  • Common Lisp ist keine „rein funktionale“ Sprache, sondern unterstützt alle Paradigmen. Es erlaubt auch solche seltsamen Dinge
    (let ((pair (cons 1 nil)))
    (setf (cdr pair) pair)
    (list (first pair) (second pair) (third pair)))
    ;; => (1 1 1)
  • Wenn ich will, dass etwas „für immer läuft“, schreibe ich es in Rust
    Wenn ich will, dass etwas „für immer kompiliert“, schreibe ich es in ANSI C
    Wenn ich will, dass etwas „für immer überlebt“, schreibe ich es in Python 2.7 und mache es zum Rückgrat der Infrastruktur-Templates der ganzen Organisation. Zusatzpunkte, wenn es ein eigenes Ansible-Modul ist
  • Wie in „Es ist 2023, also lerne ich natürlich Common Lisp“ lerne ich es auch gerade, aber die genannten Features fühlen sich für mich noch nicht nützlich an. Vielleicht ändert sich meine Meinung, wenn ich mich daran gewöhnt habe
    Was ich mir wünsche, ist, dass Lisp Kleinkram und Boilerplate minimiert oder abstrahiert und es dadurch leichter macht, Gedanken als Code auszudrücken. Für mich wirken Lisp-Ausdrücke wie die natürlichste Art, Gedanken auszudrücken
    Ich interessiere mich vor allem dafür, GUI-Apps zu schreiben, und möchte deshalb Clog lernen oder einen guten Wrapper über einem GUI-Toolkit finden oder entwickeln
  • Der Kern ist: Scheme und Janet sind ebenfalls großartig, aber der Autor möchte eine stärker eigenständige Sprache. Den Unterschied macht der Breakloop: eine vollständige REPL, die sich öffnet, wenn ein Programmfehler auftritt. Nicht Stacktrace, nicht Debugger, sondern direkt an der aktuell kaputten Stelle weiterbauen
    • Das klingt wirklich großartig, aber warum ist Common Lisp dann nicht die populärste Sprache? Ich frage als jemand, der kaum Code schreibt
    • Als Clojure-Entwickler klingt das interessant, aber ich frage mich, was in folgendem Fall passiert. Das ist kein lispiger Code, sondern nur ein Beispiel, um zu zeigen, wo ich es nicht verstehe
      (do-it (do-it first))
      (do-it first) funktioniert gut, aber was passiert, wenn der Aufruf (do-it (do-it first)) fehlschlägt?
      Es wäre gut, wenn man an der kaputten Stelle die Kontrolle bekommt und die Definition von do-it reparieren kann. Aber wenn man sie repariert, ändert sich auch das Ergebnis von (do-it first)
      Dann befindet sich die Maschine an einer Stelle, die mit dem aktuellen Code nicht mehr erreichbar ist
      Ich verstehe wirklich nicht, wie das funktionieren soll, wenn eine Änderung, die das Fortsetzen ermöglichen soll, genau den Zustand verändert, in dem man überhaupt erst die Kontrolle zum Reparieren bekommen hat
    • Wenn ich höre, dass an der Fehlerstelle eine vollständige REPL geöffnet wird und man nicht über Stacktrace oder Debugger arbeitet, sondern direkt an der aktuell kaputten Stelle weiterbaut, bekomme ich Lust, ein kleines Smalltalk-Image zu öffnen
    • Das ist auch in Gambit Scheme Standard
    • Werden solche Dinge in Produktion nicht zu Angriffswegen für Schadcode oder Sicherheitslücken?