1 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • ClojureScript 1.12.145 ändert den Compiler so, dass Funktionen mit dem Hinweis ^:async als JavaScript-async function ausgegeben werden
  • Damit lassen sich ClojureScript-Funktionen schreiben, die mit await auf Promise-Werte warten, was die JavaScript-Interoperabilität verbessert
  • Auch in Tests kann ^:async verwendet werden, und mit await lassen sich Ergebnisse asynchroner Funktionsaufrufe prüfen
  • In der jüngsten Clojure-Umfrage machte die Unterstützung von async functions den größten Anteil unter den gewünschten ClojureScript-Verbesserungen rund um die JavaScript-Interoperabilität aus
  • In typischen Fällen beim Umgang mit aktuellen Browser-APIs und beliebten Bibliotheken sinkt der Bedarf an zusätzlichen Abhängigkeiten; die vollständige Liste der Änderungen ist im Eintrag 1.12.145 des ClojureScript-Changelogs zu finden

Verwendung von ^:async und await

  • ClojureScript 1.12.145 ändert den Compiler so, dass Funktionen mit dem Hinweis ^:async als JavaScript async function ausgegeben werden
  • Da ClojureScript nun auf ECMAScript 2016 abzielt, können Verbesserungen bei der JavaScript-Interoperabilität gezielter ausgewählt werden
  • Es ist nun möglich, ClojureScript-Funktionen zu schreiben, die mit await auf Promise-Werte warten
    (refer-global :only '[Promise])
    
    (defn ^:async foo [n]
      (let [x (await (Promise/resolve 10))
            y (let [y (await (Promise/resolve 20))]
                (inc y))
            ;; not async
            f (fn [] 20)]
        (+ n x y (f))))
    
  • Auch in Tests kann ^:async verwendet werden, und mit await lassen sich die Ergebnisse asynchroner Funktionsaufrufe prüfen
    (deftest ^:async defn-test
      (try
        (let [v (await (foo 10))]
          (is (= 61 v)))
        (let [v (await (apply foo [10]))]
          (is (= 61 v)))
        (catch :default _ (is false))))
    

Hintergrund und Liste der Änderungen

  • In der jüngsten Clojure-Umfrage machte die Unterstützung von async functions den größten Anteil unter den gewünschten ClojureScript-Verbesserungen rund um die JavaScript-Interoperabilität aus
  • Durch diese Verbesserung sinkt in typischen Fällen beim Umgang mit aktuellen Browser-APIs und beliebten Bibliotheken der Bedarf an zusätzlichen Abhängigkeiten
  • Die vollständige Liste an Korrekturen, Änderungen und Verbesserungen ist im Eintrag 1.12.145 des ClojureScript-Changelogs zu finden
  • An ClojureScript 1.12.145 hat das Community-Mitglied Michiel Borkent mitgewirkt

1 Kommentare

 
GN⁺ 3 시간 전
Hacker-News-Kommentare
  • borkdude hat diesen Thread gepostet, und ich habe gesehen, dass er auch als Mitwirkender an diesem Release aufgeführt ist.
    Die Argumente gegen async/await-Unterstützung waren schon lange im Wesentlichen zwei: dass dafür tiefgreifende Änderungen im gesamten CLJS-Compiler nötig wären, und dass Makros aus Bibliotheken wie Promesa bereits einen ähnlichen Komfort bieten.
    Daneben gab es auch Stimmen, die meinten, man könne einfach core.async verwenden oder dass eine ausdrucksorientierte Sprache nicht gut zu async/await passe, aber das waren eher individuelle Ansichten als die wiederholt vorgebrachten Hauptargumente im Forum.
    borkdude hatte im Clojurians Slack einmal gesagt, dass er nicht überzeugt sei, dass das Hinzufügen von Support unrealistisch sei, und es wirkt so, als hätte er sich letztlich die Zeit genommen, es umzusetzen — dafür bin ich wirklich dankbar.

    • Borkdude hat async/await zuerst in seiner eigenen alternativen ClojureScript-Implementierung Squint umgesetzt und damit gezeigt, dass es möglich ist, und scheint das dabei Gelernte dann in den Kern-CLJS-Compiler übertragen zu haben.
  • Interessant ist, dass ClojureScript das asynchrone Paradigma schon lange vor dem Einzug von async/await in JavaScript selbst mit der Bibliothek core.async unterstützt hat.
    Das soll den Wert dieses Releases überhaupt nicht schmälern; ich meine nur, dass es ziemlich cool ist, per Hinzufügen genau einer Bibliothek als Abhängigkeit ein neues Sprachfeature nutzen zu können, das die Host-Sprache noch gar nicht hat. Clojure ist großartig.

    • Absolut. Ich habe es viel benutzt, es hat gut funktioniert, und trotz einiger kleiner Eigenheiten hatten wir damit praktisch seit über 10 Jahren async/await.
      Ich glaube, ich habe davon durch einen Vortrag von David Nolen erfahren.
      Später bin ich dazu übergegangen, im Frontend möglichst wenig JavaScript zu verwenden, und SSE ist einseitig, genau das macht es so schön. Es freut mich, dass sich derzeit Entwickler aus vielen Sprach-Communities für SSE interessieren.
      David Nolens jüngerer Vortrag „A ClojureScript Survival Kit“ war ebenfalls gut: https://youtu.be/BeE00vGC36E
      Für die Arbeit, die David „Swannodette“ Nolen seit den Anfangstagen von ClojureScript und core.async geleistet hat, kann man gar nicht dankbar genug sein. Besonders erstaunlich an diesem Vortrag ist, dass er tatsächlich auch Vorfreude auf eine Richtung zeigt, in der man ClojureScript hinter sich lässt und stattdessen pures serverseitiges Clojure, Server-Sent Events und nur eine winzige Menge JavaScript verwendet.
      Die eigentliche Demo beginnt ungefähr bei 26:30. Nachdem er den Ressourcenverbrauch einer im Client laufenden Web-App zeigt, demonstriert er dieselbe Web-App auf dem Server mit einseitigem Push per SSE zum Client, und der Ressourcenverbrauch sinkt fast auf null — ziemlich eindrucksvoll.
      Das passt sicher nicht für alle Fälle, aber mit einer minimalen DOM-Manipulationsbibliothek wurde es deutlich leichter, über Web-App und Zustand nachzudenken. Früher musste ich sowohl eine Clojure-REPL als auch eine ClojureScript-REPL offen haben und viel bidirektionalen Traffic sowie schwer reproduzierbare Zustände handhaben; jetzt geht alles viel schneller und ist auch leichter reproduzierbar.
    • Stimmt, aber im Jahr 2026 gibt es auch viele Gründe, core.async zu vermeiden.
      Das JavaScript-Output wird größer, es gibt kein eingebautes Fehlermodell, und wenn etwas schiefgeht, wird der Code in schwer lesbare und schwer zu debuggende Zustandsmaschinen umgewandelt.
      Außerdem kann das go-Makro keinen Code außerhalb seines eigenen S-Ausdrucks transformieren, was dazu verleitet, Funktionen unnötig groß werden zu lassen.
      Wie jemand von Cognitect einmal sagte: „core.async ist wunderschöner Unsinn“.
  • Es überrascht mich, dass Clojure/ClojureScript in letzter Zeit plötzlich häufiger in sozialen Medien auftaucht.
    Ich habe es um 2012 herum einige Jahre beruflich genutzt, bin dann aber wie viele andere vom JVM weg zu getypten funktionalen Sprachen gewechselt.
    Liegt das wachsende Interesse heute an agentischem Coding? Weil es keine Typprüfung gibt und weniger falsch geschriebene Syntaxfehler oder reservierte Wörter, sodass man Code schneller überfliegen kann? Erleben S-Ausdrücke gerade ein Comeback?

    • Ich persönlich bin von getypten funktionalen Sprachen zu ClojureScript und dann vor etwa 10 Jahren zu Clojure gewechselt.
      Die ernsthaften Clojure-Codebasen, die ich kenne, investieren stark in Testsuiten, sodass man mit ein paar Techniken, um einer KI den effektivsten Umgang mit der Testsuite beizubringen, ziemlich gut vorankommen kann.
      Einige Kollegen lassen ihre Agenten auch mit der REPL interagieren, was schneller sei, weil nicht jedes Mal die Startkosten anfallen. Dafür war ich bisher zu faul, aber schon jetzt ist es schnell genug.
      Clojure hat wenig Reibung. Alles außer false und nil ist wahr, es gibt keine Operatorprioritätstabelle, und die Kernsprache unterstützt unveränderliche, persistente Datenstrukturen standardmäßig.
      Alles ist ein Ausdruck, keine Struktur, in der Operatoren und Ausdrücke vermischt sind. map, reduce, filter sind eingebaut und werden im normalen Code selbstverständlich verwendet.
      Clojure-Code, den ich vor 10 Jahren geschrieben habe, läuft heute mit hoher Wahrscheinlichkeit noch, und Ökosystem sowie Sprachdesigner behandeln es fast wie ein Tabu, bestehenden Code zu brechen.
      Von allen Sprachen, die ich verwendet habe, ließ sich darin am freiesten ausdrücken, bei gleichzeitig den wenigsten Kopfschmerzen. Flowstorm, praktisch ein Rückwärts-Debugger, ist außerdem ein traumhaftes Werkzeug fürs Programmieren.
      Wenn man zufrieden arbeiten will, ist es eine wirklich gute Sprache. Umgekehrt nehmen die meisten Nutzer das einfach als selbstverständlich hin und reden deshalb nicht viel darüber.
      Unter Programmierern, die Clojure kommerziell einsetzen, gibt es auch viele, die die Sprache nicht wirklich gut verstehen und deshalb nicht besonders glücklich damit sind. Oft haben sie sie nicht selbst gewählt oder waren noch nicht so weit, und ich denke, man muss vor Clojure ungefähr 10 Jahre lang die Dinge erlebt haben, die man in anderen Sprachen nervig fand.
      Die Software-Videos von Clojure-Erfinder Rich Hickey sind zwar berühmt und einflussreich, aber das heißt nicht, dass Kollegen sie gesehen hätten oder sich dafür interessieren.
    • Der jüngste Interessensanstieg dürfte stark mit der Veröffentlichung der Clojure-Dokumentation zusammenhängen: https://clojure.org/about/documentary
    • Ein weiteres Merkmal, das gut zu agentischem Coding passt, ist REPL-getriebene Entwicklung. Ich weiß nicht, warum dieser Ansatz sich in anderen Sprachen, die ihn theoretisch unterstützen könnten, nicht weiter verbreitet hat.
    • Ich habe agentisches Coding in mehreren Sprachen ausprobiert, und getypte Sprachen passen deutlich besser dazu. Das Typsystem korrigiert die von Agenten halluzinierten Fehler praktisch mit, besonders bei größeren Refactorings.
      Eine große ungetypte Python-Codebasis mit KI zu bearbeiten war mühsam. Bei allem, was nicht durch Tests abgedeckt war, war es zu langweilig zu prüfen, ob wirklich nichts kaputtgegangen ist.
      Je stärker das Typsystem, desto besser. Außerdem werden KI-Modelle auf Code trainiert, daher liegt nahe, dass sie mit populäreren Sprachen besser umgehen. ClojureScript ist gut, aber keine Mainstream-Sprache, daher würde ich erwarten, dass KI damit schlechter performt als mit JavaScript.
      Wenn man also KI mitdenkt, ist es letztlich besser, eine getypte Sprache zu wählen — oder bei dynamischen Sprachen zumindest eine mit Type Hints.
    • Ich meine, die Dokumentation ist erschienen oder angekündigt worden; das könnte den Effekt erklären.
  • Das ist wirklich groß. Seit der Ankündigung von Jank gab es im Clojure-Ökosystem nicht mehr so viel Vorfreude.

  • Ich wünschte, JavaScript-Alternativen im Frontend würden nicht nur obskur bleiben, sondern sich wirklich etablieren.
    Ich würde gern etwas wie ClojureScript verwenden, aber außerhalb persönlicher Side Projects fällt es mir schwer, mir vorzustellen, wo man es einsetzen könnte. Wenn eine Organisation ohnehin schon Clojure im Backend nutzt, wäre die Einführung vielleicht leichter.

    • Ich frage mich, ob du dir Sorgen machst, dass Kollegen die Sprache nicht kennen, weil sie nicht Mainstream ist, oder ob du Angst hast, dass die Sprache selbst aufgegeben wird oder schlecht ist.
      In Produktion habe ich es nicht eingesetzt, aber ich habe ein paar Side Projects und Dinge für die Familie damit ausgeliefert. Reagent, der React-Wrapper für ClojureScript, ergab für mich ehrlich gesagt mehr Sinn als React selbst.
      Man baut HTML mit Hiccup, und Komponenten sind einfach nur Funktionen innerhalb eines Hiccup-DSL, das selbst im Grunde ebenfalls nur aus Listen besteht, wodurch das Ergebnis sehr sauber wird. Statisches sieht statisch aus, Dynamisches klar dynamisch, und es fühlte sich nach viel weniger Magie an als gewöhnliches React.
      Unangenehm wurde es nur, wenn ich nicht-funktionale Komponenten aus NPM verwenden wollte. Nicht fatal, aber der Code wurde hässlich. Man konnte das mit Wrappern beheben, aber einige JS-Bibliotheken sind in cljs von Haus aus ziemlich chaotisch.
    • Du musst keine Angst haben, es ist wirklich gut. Ich nutze es seit 10 Jahren, um komplexe Apps in hochoptimierten Client-Code zu kompilieren, und ich würde es nicht als obskur bezeichnen.
      Die Community ist außerdem sehr freundlich und reif.
    • Nicht nur vorstellen — fang klein an. Wenn dein Team Bash-Skripte verwendet, könntest du sie mit Babashka neu schreiben.
      Schreib zuerst ein paar persönliche Skripte um, entwickle ein Gefühl dafür und spüre dann die Vorteile. Es ist nicht in jedem Fall besser, aber wenn später Leute dich um Rat fragen, solltest du selbst überzeugt genug sein.
      Wenn man eine fremde Technologie einführt, ist es gut, etwas weniger Wichtiges auszuwählen, es neu zu schreiben und dann einfach laufen zu lassen. Falls es Probleme gibt, kann man leicht zurückrudern; wenn die Leute anfangen, es zu mögen, kann man es schrittweise ausweiten.
      Als ich früher F# heimlich in eine .NET-Organisation eingeführt habe, habe ich auch zuerst weniger wichtige Tests in F# geschrieben.
    • Schau dir Gleam an. Ich benutze es mit großer Zufriedenheit in Produktion.
      https://blisswriter.app/
      https://blog.nestful.app/p/how-we-dropped-vue-for-gleam-and
    • Nachdem ich https://hypermedia.systems/ gelesen hatte, kam ich zu dem Schluss, dass das beste Frontend gar kein Frontend ist.
  • Ich habe cljs schon länger nicht mehr verfolgt, aber ursprünglich wurde es meiner Erinnerung nach ungefähr als „Clojure auf JavaScript“ beschrieben. Zumindest, soweit ich mich erinnere, hat Rich es anfangs so erklärt.
    Ich verstand die Absicht so, dass es möglichst nah an einer weiteren Runtime bleiben sollte.
    Diese Änderung hier wirkt aber so, als würde sie ein Feature hinzufügen, das es nur in cljs gibt, und await kollidiert zudem mit dem bestehenden Keyword in clojure.core.
    Ich frage mich, ob die beiden Implementierungen sich mit der Zeit auseinanderentwickelt haben oder ob dieses Feature den Nutzern so wichtig war, dass ihnen der Unterschied das wert war.

  • Es ist wichtig, weil man JavaScript-Interop ohne zusätzliche Bibliothek handhaben kann.
    Dieses Feature hat lange gefehlt, daher ist dieses Release ziemlich willkommen.

  • Es scheint mir die bessere Verarbeitungsweise zu sein, async/await-Funktionen in CSP einzupacken. Clojure hatte dafür bereits ein besseres Muster.

    • Dieses Release dreht sich darum, die nativen Fähigkeiten der Host-Sprache in ClojureScript offenzulegen.
      core.async verschwindet dadurch nicht, und wenn async/await besser passt als eine Promise-basierte Umsetzung, könnte auch der .cljs-Teil von core.async aktualisiert werden.
    • Das kann man weiterhin so machen. In der ClojureScript-Welt ist das schon seit Jahren möglich, und letztlich sind das ohnehin nur Promises.
      Ich glaube nicht, dass diese neue Funktionsmarkierung diesen Ansatz verdrängen wird.
      https://clojurescript.org/guides/promise-interop#using-promi...
  • Ich weiß nicht recht, wie ich das einordnen soll. Einer der Kerngedanken von core.async war doch, all diese Dinge in Channels zu schieben, oder?
    Ich bin nicht sicher, ob ein JavaScript-artiges async-Keyword wirklich ein Upgrade ist.

    • Es geht hier darum, JS-Features zu nutzen, ohne eine zusätzliche Abhängigkeit wie core.async hereinzuziehen.
      Man muss es nicht verwenden, und man kann weiterhin core.async nutzen. In der letzten ClojureScript-Umfrage war das außerdem das am häufigsten gewünschte Feature.