2 Punkte von GN⁺ 2024-05-27 | 1 Kommentare | Auf WhatsApp teilen
  • Hurl ist eine experimentelle Programmiersprache, die den Kontrollfluss ausschließlich über Exception Handling gestaltet – statt über gewöhnliche Verzweigungen oder Schleifen
  • Entstanden ist sie aus einem Gespräch zwischen Nicole Tietz-Sokolskaya und Freundinnen und Freunden vom Recurse Center; die Website bietet Dokumentation zur Nutzung, Beispiele, Debugging-Hinweise sowie Fragen und Antworten
  • Die Testimonials auf der Einführungsseite stellen bewusst einen scherzhaften Ton in den Vordergrund, etwa „monstrosity is beautiful“ und „Certified unhinged™“
  • Der Quellcode von Hurl und der Website ist öffentlich, aber wer Patches per E-Mail einreichen möchte, muss die Rechte am Patch abtreten
  • Als Lizenz kann zwischen AGPL-3.0, GAL-1.0 und einer kommerziellen Lizenz gewählt werden

Ein Sprachexperiment, bei dem nur Exception Handling übrig bleibt

  • Hurl wurde für einen einzigen Zweck geschaffen: zu erkunden, ob eine Sprache möglich ist, die ausschließlich auf Kontrollfluss auf Basis von Exception Handling beruht
  • Die Idee entstand aus einem Gespräch zwischen Nicole Tietz-Sokolskaya und Freundinnen und Freunden vom Recurse Center; deren Identität wird „der Würde halber“ nicht offengelegt
  • Die Website bietet Dokumentation zur Nutzung von Hurl, Beispiele, Debugging-Hinweise sowie Fragen und Antworten

Sieht wie ein Scherz aus, ist aber ein tatsächlich veröffentlichtes Projekt

  • Auf der Einführungsseite stehen Testimonials, die den Charakter von Hurl zeigen
    • „This monstrosity is beautiful, and I must never touch it“
    • „Certified unhinged™!“
    • „is "🤮" an available quote?“
  • Wer weitere Testimonials aufnehmen lassen möchte, muss Nicole per E-Mail kontaktieren; für die Aufnahme des Zitats ist eine ausdrückliche Zustimmung erforderlich

Quellcode und Lizenzbedingungen

  • Der Quellcode der Sprache Hurl und dieser Website ist in Hurl's repo öffentlich verfügbar
  • Wer Bugs oder Fehler findet, kann Patches per E-Mail senden, muss aber alle Rechte am Patch abtreten
    • Diese Bedingung dient dazu, die Möglichkeit einer Relizenzierung und einer kommerziellen Lizenz zu erhalten
  • Das Projekt kann unter einer der folgenden drei Lizenzen genutzt werden
    • AGPL-3.0

      • GAL-1.0: Gay Agenda License
      • Kommerzielle Lizenz
      • Im Zuge der Lizenzprüfung wurden auch joke licenses und unfortunate licenses in Betracht gezogen, letztlich werden jedoch die drei oben genannten Lizenzen verwendet

1 Kommentare

 
GN⁺ 2024-05-27
Hacker-News-Kommentare
  • Wenn ich eine Programmiersprache entwerfen würde, würde ich bei include/import Namespaces erzwingen und möglichst auch Nebenwirkungen auf Top-Level-Ebene verhindern
    Wenn man etwas wie let foo = include "lib/foo.hurl" bekommt und dann foo.init() aufruft, lässt sich viel leichter nachvollziehen, was passiert
    Wenn dagegen auf include "lib/foo.hurl" // side effects direkt baz(buz) folgt, ist schwer zu erkennen, ob die Funktionen und Variablen aus der Standardbibliothek stammen oder irgendwo per include hereingekommen sind

    • Falls es keine explizite Namensbindung gibt, sollte man besser erzwingen, dass Import-Anweisung und Namespace übereinstimmen
      import "foo/bar" sollte also foo.* oder bar.* verfügbar machen, aber nicht plötzlich bazz.*. Das erinnert direkt an Go
    • Ich widerspreche nicht unbedingt, aber das IntelliJ, das ich bei der Arbeit nutze, zeigt sehr klar, woher ein bestimmter Import kommt, und man kann per Shortcut direkt dorthin springen
      VSCode kann mit Plugins und LSP etwas Ähnliches, ist aber deutlich schlechter. Die Code-Navigation ist so langsam, dass ich mit VSCode nicht arbeiten könnte
      Ich frage mich, ob solche Vorschläge nur dann nützlich sind, wenn man solche Tools nicht hat. Zumindest im professionellen Umfeld scheint es unmöglich, ohne solche Werkzeuge auszukommen
    • So könnte man an foo.init() auch Parameter übergeben, was mit einem nackten Import nicht geht
    • Wenn eine Sprache bei der Ablaufsteuerung auf Ausnahmen basiert, ist der Zug in Sachen „leicht nachvollziehbar“ wohl ohnehin schon abgefahren
      Das heißt nicht, dass dieses Projekt wertlos ist, eher im Gegenteil: Ich sehe es als Kunstwerk
    • Stimme zu 100 % zu
      Ich habe einmal Ruby geforkt, damit require nicht mehr die Symboltabelle überschreibt, aber das Ruby-Ökosystem schien mir zu stark von gemeinsam genutztem globalem veränderlichem Zustand abhängig zu sein, sodass ich am Ende das Interesse an Ruby selbst verloren habe
  • Ausnahmen fand ich schon immer unerquicklich, weil sie es schwer machen, den Vertrag zwischen Aufrufer und Aufgerufenem zu verstehen, und weil sie die Kopplung im Code erhöhen
    Ich bevorzuge den Ansatz von Go oder Rust, so etwas über Rückgabewerte zu behandeln. Nach einem schnellen Blick auf die Sprache ist mir nicht klar, ob es hier etwas gibt, das dieses Problem löst
    Wenn eine IDE dynamisch alle nicht abgefangenen Ausnahmen einer Funktion ermitteln und zu den Stellen springen könnte, an denen eine Ausnahme ausgelöst werden kann, wäre so ein Modell vielleicht okay. Wie man dann aber die Kopplung in den Griff bekommt, weiß ich nicht, und der Kontrollflussgraph dürfte extrem instabil werden

    • In Java macht IntelliJ genau das. Wenn es eine Funktion gibt, die eine Ausnahme wirft, und diese irgendwo im Projekt beim Aufrufer nicht abgefangen wird, wird das als Problem markiert, und man kann leicht zur Implementierung oder zur Aufrufstelle springen
      In Java sind die Ausnahmen, die eine Funktion werfen kann, Teil der Funktionssignatur, sofern sie nicht von RuntimeException erben. In solchen Fällen kompiliert der Code nicht, wenn man eine Ausnahme wirft, ohne sie in die Signatur aufzunehmen
      Diese Eigenschaft von Java macht es für IDEs viel einfacher, nicht abgefangene Ausnahmen zu melden, aber bei Nicht-Runtime-Exceptions ist das letztlich ein Problem, das sich per statischer Analyse lösen lässt
      Dagegen wirkt der Ansatz, standardisierte Ok-/Err-Wrapper zurückzugeben, sowohl bei der Tool-Unterstützung als auch beim Komfort für Entwickler einfacher
    • Zwischen dem Werfen einer Ausnahme und dem Zurückgeben einer Ausnahme als Variable gibt es buchstäblich überhaupt keinen Unterschied
      Der einzige Unterschied ist, dass man beim Weiterreichen von Ausnahmen den Boilerplate-Code, den der Compiler erledigen könnte, von Hand schreiben muss
      Ich kann wirklich nicht nachvollziehen, warum im Jahr 2024 irgendjemand bei klarem Verstand das manuell machen wollen sollte
    • Go würde ich wohl mehr mögen, wenn es Fehlkontext nicht so selbstverständlich verschlucken würde
      In der Zeit, in der ich es genutzt habe, war es viel schmerzhafter, ohne Debugger die Root Cause zu finden
  • Im toss-Beispiel steht, es werde „hauptsächlich verwendet, um mehrere Werte außerhalb einer Funktion zu übergeben, sei nicht unbedingt nötig, aber irgendwie niedlich“, doch nutzlos ist es keineswegs, sondern eher eine Implementierung von fortsetzbaren Generatoren
    Natürlich könnte es ziemlich interessant werden, wenn man damit auch etwas anderes tun würde als nur sofort wieder fortzusetzen. Man müsste lediglich die gesamte Codebasis als einen inneren und äußeren toss-Stack organisieren

    • Es ist nicht exakt dasselbe. Fortsetzbare Generatoren kann man an jedem späteren Punkt des Programms wieder aufnehmen, aber hier muss return auf der Handler-Seite lexikalisch gescopet sein
      In Python kann man zum Beispiel überall next() aufrufen
      Das hier ist eher damit vergleichbar, einen Callback über einen Nebenkanal zu übergeben. toss ruft diesen Callback auf, und return kehrt dann buchstäblich von dort zurück
    • Genau das war auch mein erster Gedanke nach dem Lesen
      In anderen Sprachen wäre das eine wirklich nützliche Funktion, hier wird sie dagegen eher wie ein niedliches Feature behandelt, auf das man auch verzichten könnte. Fast schon ein Witz
    • Das ist eher stackbasierte Event-Propagation
    • So etwas wie yield in C#?
    • Gleicher erster Gedanke. Eine kleine, coole Sprache mit Generatoren klingt doch gut
  • Hurl scheint ziemlich nahe am Condition-System von Smalltalk oder Common Lisp zu sein
    Stack-Unwinding und Fortsetzen sind nur zwei mögliche Neustarts: https://gigamonkeys.com/book/beyond-exception-handling-condi...

  • Unabhängig vom Projekt selbst bin ich fest davon überzeugt, dass die Welt besser wäre, wenn mehr Dinge in ihrer Domain die .wtf-Erweiterung verwenden würden

  • Das klingt nach einer schwachen Form von algebraischen Effekten, aber es ist trotzdem cool, solche Sprachen und die Möglichkeiten damit zu sehen

    • Algebraische Effekte habe ich nie wirklich verstanden, aber die Hurl-Dokumentation verstehe ich
      Falls algebraische Effekte im Grunde so etwas wie das toss-Keyword von Hurl sind, würde mich interessieren, worin sie mächtiger sind als toss
  • Ein interessantes Gedankenexperiment
    Ich hasse Ausnahmen wirklich und möchte eine Sprache ohne Ausnahmen
    Ausnahmen sind das goto unserer Zeit
    Solange es Maybe/Option und Effect/Result gibt, gibt es kaum einen Grund, Ausnahmen zu werfen und im Kopf nachzuverfolgen, wo sie behandelt werden
    Ich bin etwas besorgt, dass algebraische Effekte populärer werden und sogar das ohnehin schon viel zu komplexe Frontend-JS beeinflussen könnten. Denn sie fördern letztlich den Ansatz, zur Ablaufsteuerung „Ausnahmen“ zu werfen
    Wenn das alles nur dazu dient, das Color-Problem von asynchron und synchron zu vermeiden, ist es das meiner Meinung nach überhaupt nicht wert

  • Wow, ich hasse das. Aber irgendwie hat es fast etwas Elegantes
    Es ist sehr schwer, es im Kopf zu modellieren, aber trotzdem
    Etwas ernsthafter gesprochen: Ich hätte gern syntaktisch unterschiedliche catch-Konstrukte für fortsetzbare und nicht fortsetzbare Ausnahmen. Dann gäbe es keine syntaktische Mehrdeutigkeit mehr darüber, ob return den Kontrollfluss zum Werfer der nächstgelegenen unmittelbaren Ausnahme zurückschickt oder nicht
    Außerdem sollte die Standardbibliothek nicht auf gewöhnliche Funktionen mit Rückgabewerten ausweichen. Nur weil einem das eigene Essen auf den Magen schlägt, muss man es nicht gleich aufgeben

    • Stimmt. Eine andere Möglichkeit wäre allerdings, dass die Syntax beim Aufruf einer Funktion an einer Stelle, an der ein Wert erwartet wird, automatisch ein catch macht
  • Habe ich das richtig verstanden, dass man hurltes abfangen kann, tosstes aber nicht? Daran müsste ich mich erst gewöhnen
    Ich frage mich außerdem, wie viel Hurl ich schreiben muss, bis die Leute anfangen, mich einen Tosser zu nennen

  • Toss klingt nach einem interessanten Sprachkonstrukt. Es läuft den Stack hinauf, sucht einen Exception-Handler und kehrt dann an die ursprüngliche Stelle zurück, um die Ausführung fortzusetzen, als wäre nichts passiert
    Damit ließe sich zur Laufzeit offenbar zusätzliches Verhalten einschleusen
    Normalerweise macht man in objektorientiertem Code Dependency Injection über Service-Konstruktoren, aber ermöglicht toss so etwas über einen „toss-Handler“?

    • Schau dir Koka an. Das ist eine „echte“ Sprache mit einem System algebraischer Effekte
      https://koka-lang.github.io/koka/doc/book.html#why-handlers
    • Das ist dem Condition-System von Common Lisp ziemlich ähnlich
      Ich kenne mich damit nicht besonders gut aus, aber ich weiß, dass man auf diese Weise Verhalten zur Laufzeit einschleusen kann
    • Ähnlich wie Resume Next in VB