Introduction d’Async/Await dans ClojureScript
(clojurescript.org)- ClojureScript 1.12.145 modifie le compilateur pour émettre les fonctions annotées avec l’indication
^:asyncsous forme deasync functionJavaScript - Il devient possible d’écrire des fonctions ClojureScript qui attendent des valeurs Promise avec
await, ce qui améliore l’interopérabilité avec JavaScript ^:asyncpeut aussi être utilisé dans les tests, etawaitpermet de vérifier le résultat d’appels de fonctions asynchrones- Dans la récente enquête Clojure, la prise en charge des async functions représentait la demande la plus importante parmi les améliorations souhaitées pour l’interopérabilité JavaScript de ClojureScript
- Dans les cas courants impliquant les API de navigateurs modernes et les bibliothèques populaires, le besoin d’ajouter des dépendances supplémentaires diminue, et la liste complète des changements est disponible dans l’entrée 1.12.145 du changelog ClojureScript
Utilisation de ^:async et await
- ClojureScript 1.12.145 modifie le compilateur pour émettre les fonctions annotées avec l’indication
^:asyncsous forme de JavaScript async function - Avec le ciblage par ClojureScript de ECMAScript 2016, il devient possible de choisir plus soigneusement les domaines d’amélioration de l’interopérabilité JavaScript
- Il est désormais possible d’écrire des fonctions ClojureScript qui attendent des valeurs
Promiseavecawait(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)))) ^:asyncpeut aussi être utilisé dans les tests, etawaitpermet de vérifier le résultat d’appels de fonctions asynchrones(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))))
Contexte et liste des changements
- Dans la récente enquête Clojure, la prise en charge des async functions représentait la demande la plus importante parmi les améliorations souhaitées pour l’interopérabilité JavaScript de ClojureScript
- Cette amélioration réduit le besoin d’ajouter des dépendances supplémentaires dans les cas courants impliquant les API de navigateurs modernes et les bibliothèques populaires
- La liste complète des correctifs, modifications et améliorations est disponible dans l’entrée 1.12.145 du changelog ClojureScript
- Michiel Borkent, membre de la communauté, a contribué à ClojureScript 1.12.145
1 commentaires
Avis sur Hacker News
J’ai vu que borkdude avait posté ce fil, et qu’il est aussi listé parmi les contributeurs de cette release
Depuis longtemps, les arguments contre la prise en charge de async/await étaient globalement de deux ordres : cela nécessiterait des changements profonds dans tout le compilateur CLJS, et des macros de bibliothèques comme Promesa offraient déjà un confort similaire
Il y avait aussi des avis disant qu’il suffisait d’utiliser core.async, ou qu’un langage orienté expressions se marie mal avec async/await, mais c’étaient davantage des opinions personnelles que les arguments dominants répétés sur les forums
Sur le Slack des Clojurians, borkdude avait déjà dit qu’il n’était pas convaincu que l’ajout du support soit irréaliste, et il semble qu’il ait fini par prendre le temps de l’implémenter, donc un grand merci à lui
Fait amusant : ClojureScript prenait déjà en charge le paradigme asynchrone via la bibliothèque core.async bien avant que JavaScript lui-même n’intègre async/await
Ce n’est absolument pas pour minimiser l’importance de cette release, mais plutôt pour dire qu’il est remarquable de pouvoir utiliser une nouvelle fonctionnalité du langage, absente du langage hôte, simplement en ajoutant une bibliothèque aux dépendances. Clojure est formidable
Je crois l’avoir découvert via une présentation de David Nolen
Ensuite, je me suis orienté vers un frontend avec un minimum de JavaScript, et SSE est magnifique justement parce que c’est unidirectionnel. Ça fait plaisir de voir des développeurs de plusieurs écosystèmes s’y intéresser aujourd’hui
La présentation récente de David Nolen, “A ClojureScript Survival Kit”, était aussi excellente : https://youtu.be/BeE00vGC36E
On ne remerciera jamais assez David “Swannodette” Nolen pour tout ce qu’il a fait depuis les débuts de ClojureScript et de core.async. Ce qui est particulièrement frappant dans cette présentation, c’est qu’il semble réellement enthousiaste à l’idée de délaisser ClojureScript au profit de Clojure pur côté serveur, avec des server-sent events et très peu de JavaScript
La démo proprement dite commence vers 26:30. Après avoir montré l’utilisation des ressources d’une webapp exécutée côté client, il montre la même application exécutée côté serveur, poussée en unidirectionnel vers le client via SSE, et l’usage des ressources tombe presque à zéro, ce qui est assez marquant
Ce n’est pas adapté à tous les cas, mais avec une bibliothèque minimale de manipulation du DOM, il est devenu plus facile de raisonner sur l’application web et son état. Avant, il fallait lancer à la fois un REPL Clojure et un REPL ClojureScript, gérer beaucoup de trafic bidirectionnel et des états difficiles à reproduire ; maintenant c’est bien plus rapide et bien plus reproductible
Le JavaScript généré est plus volumineux, il n’y a pas de modèle d’erreur intrinsèque, et quand quelque chose se passe mal, le code se transforme en machine à états difficile à lire et à déboguer
En plus, la macro
gone peut pas transformer du code en dehors de sa propre S-expression, ce qui pousse à écrire des fonctions démesurément grossesComme l’a dit quelqu’un chez Cognitect, « core.async est un magnifique non-sens »
C’est surprenant de voir Clojure/ClojureScript réapparaître soudainement plus souvent sur les réseaux sociaux
Je l’ai utilisé au travail pendant quelques années vers 2012, mais comme beaucoup d’autres, j’ai quitté la JVM pour des langages fonctionnels typés
Est-ce que cet intérêt récent vient du coding agentique ? Peut-être parce qu’il n’y a pas de vérification de types, moins d’erreurs de syntaxe absurdes ou de mots réservés, donc le code se parcourt plus vite ? Serait-ce le retour des S-expressions ?
Les codebases Clojure sérieuses que je connais investissent beaucoup dans leur suite de tests, donc si on ajoute simplement des techniques pour apprendre à l’IA à utiliser cette suite de tests de la manière la plus efficace possible, on peut déjà aller assez loin
Certains collègues font aussi interagir les agents avec le REPL, et comme ils n’ont pas à repayer le coût de démarrage à chaque fois, c’est plus rapide. Je n’ai pas poussé jusque-là par paresse, mais même comme ça c’est déjà suffisamment rapide
Clojure a peu d’éléments gênants. Tout est vrai sauf
falseetnil, il n’y a pas de table de priorité des opérateurs, et le cœur du langage fournit nativement des structures de données immuables et persistantesTout est expression, pas un mélange d’opérateurs et d’expressions.
map,reduceetfiltersont intégrés et utilisés naturellement dans le code courantDu code Clojure écrit il y a 10 ans a encore de bonnes chances de fonctionner aujourd’hui, et dans l’écosystème comme chez les concepteurs du langage, casser le code est presque tabou
C’est le langage que j’ai utilisé où j’ai eu le plus de liberté pour exprimer des idées et le moins de maux de tête. Flowstorm, qui fait office de débogueur inversé de facto, est aussi un outil de rêve
C’est un excellent langage pour qui veut travailler sereinement. En revanche, beaucoup d’utilisateurs considèrent cela comme normal, donc ils en parlent peu
Parmi les programmeurs qui utilisent Clojure en contexte commercial, beaucoup ne semblent pas vraiment heureux, souvent parce qu’ils ne l’ont pas choisi eux-mêmes ou n’étaient pas encore prêts, et je pense qu’il faut avoir passé une dizaine d’années à subir ce qu’on détestait dans d’autres langages avant d’apprécier Clojure à sa juste valeur
Les vidéos de Rich Hickey sur le logiciel sont célèbres et influentes, mais cela ne veut pas dire que vos collègues les aient vues ou s’y intéressent
Gérer avec l’IA une grosse codebase Python non typée a été pénible. Vérifier que rien n’a cassé dans les parties non couvertes par les tests est trop fastidieux
Plus le système de types est fort, mieux c’est. De plus, les modèles d’IA sont entraînés sur du code, donc plus un langage est populaire, meilleures sont probablement leurs performances. ClojureScript est bien, mais ce n’est pas un langage grand public, donc je m’attendrais à de moins bonnes performances de l’IA qu’en JavaScript
Au final, si l’on pense à l’IA, mieux vaut choisir un langage typé, ou à défaut un langage dynamique avec des annotations de types
C’est vraiment énorme. On n’avait pas vu autant d’enthousiasme dans l’écosystème Clojure depuis l’annonce de Jank
J’aimerais vraiment qu’une alternative à JavaScript côté frontend dépasse le stade confidentiel et s’impose réellement
J’aimerais essayer quelque chose comme ClojureScript, mais j’ai du mal à imaginer où l’utiliser en dehors d’un projet perso. J’imagine que c’est plus facile à adopter dans une organisation dont le backend est déjà en Clojure
Je ne l’ai pas utilisé en production, mais j’ai déployé quelques side projects et des outils pour la famille. Reagent, le wrapper React de ClojureScript, m’a franchement semblé plus cohérent que React lui-même
On construit le HTML avec Hiccup, et les composants ne sont que des fonctions dans le DSL Hiccup, qui est lui-même essentiellement une liste, donc le résultat est très propre. Le statique paraît statique, le dynamique paraît clairement dynamique, et il y a bien moins de magie que dans React classique
Ce qui m’a surtout déplu, c’est quand j’ai voulu utiliser des composants non fonctionnels trouvés sur NPM. Ce n’est pas rédhibitoire, mais le code devient moche. On pouvait corriger ça avec des wrappers, mais certaines bibliothèques JS sont vraiment très sales par défaut côté cljs
La communauté est aussi très bienveillante et mature
Commencez par des scripts personnels, prenez la main, puis voyez si les avantages vous parlent. Ce n’est pas meilleur dans tous les cas, mais plus tard les gens pourraient venir vous demander conseil, donc il vaut mieux être vraiment convaincu vous-même
Quand on introduit une technologie inconnue, une bonne stratégie consiste à choisir quelque chose de peu important, le réécrire, puis le laisser vivre. Si ça pose problème, le retour en arrière est facile ; si les gens commencent à aimer, on peut étendre petit à petit
À l’époque, quand j’introduisais discrètement F# dans une organisation .NET, j’avais commencé par écrire en F# des tests peu critiques
https://blisswriter.app/
https://blog.nestful.app/p/how-we-dropped-vue-for-gleam-and
Je n’ai pas suivi cljs de près depuis longtemps, mais dans mon souvenir on le présentait à l’origine comme « Clojure sur JavaScript ». Du moins, il me semble que Rich l’expliquait comme ça au début
Je pensais que l’intention était d’en faire autant que possible un simple runtime de plus
Or ce changement a l’air d’ajouter une fonctionnalité propre à cljs, et
awaitest déjà un mot-clé declojure.core, donc cela entre aussi en conflit avec Clojure lui-mêmeJe me demande si les deux implémentations ont divergé avec le temps, ou si cette fonctionnalité était simplement assez importante pour que les utilisateurs acceptent cette différence
C’est important parce que cela permet de gérer l’interopérabilité JavaScript sans ajouter de bibliothèque supplémentaire
C’était une fonctionnalité absente depuis longtemps, donc cette release est très bienvenue
Encapsuler les fonctions async/await dans du CSP me semble être une meilleure façon de traiter le problème. Clojure avait déjà un meilleur pattern
core.async ne disparaît pas, et si async/await convient mieux que l’implémentation à base de Promise, la partie
.cljsde core.async pourra peut-être être mise à jourJe ne pense pas que cette nouvelle indication de fonction fasse disparaître cette approche
https://clojurescript.org/guides/promise-interop#using-promi...
Je ne sais pas trop comment prendre ça. L’un des objectifs de core.async, ce n’était pas justement de tout faire passer par des canaux ?
Je ne suis pas certain que disposer du mot-clé
asyncà la JavaScript soit réellement une améliorationRien n’oblige à l’utiliser, et on peut toujours continuer avec core.async. C’était aussi la fonctionnalité la plus demandée dans la récente enquête ClojureScript