Core.async n’est-il pas contraire aux principes Clojure?

J’ai vu beaucoup de programmeurs Clojure enthousiastes à propos de la nouvelle bibliothèque core.async et, même si cela semble très intéressant, j’ai du mal à voir comment elle se conforme aux principes de Clojure, alors j’ai ces questions:

  1. Il utilise un état mutable partout, comme le suggèrent les noms de fonctions en ayant un point d’exclamation, comme alt !, put !,> !, etc. Si vous mettez ou prenez une valeur d’un canal, ce canal est modifié en place. N’est-ce pas contraire à la philosophie de Clojure de préférer les structures de données immuables, les fonctions pures, etc.? Ou est-ce que core.async est conçu pour être utilisé uniquement là où les choses mutables ne peuvent pas être évitées?
  2. Depuis “go” est une macro (modifiant ainsi la structure du code) et assure “<!" est utilisé directement dans un go-block, il n'est pas possible d'utiliser "<!" à l'intérieur d'une autre fonction, comme ceci:

    (defn take-and-print [c] (println (!! ch 123) (go (take-and-print ch)) Assert failed: <! used not in (go ...) block 

    Il me semble que cela empêche la simplicité et la composition. Pourquoi n’est-ce pas un problème?

  3. Peut-être en conséquence des deux problèmes précédents, beaucoup de code avec core.async utilise des constructions de niveau inférieur telles que loop / recur au lieu de map / filter / Reduce. N’est-ce pas un pas en arrière?

Où est-ce que je manque le point?

Merci d’avance.

La première préoccupation – oui, les opérations de base sont des effets secondaires. Cependant les canaux n’ont pas les problèmes normalement associés aux références mutables car ils ne représentent pas un “lieu” – les canaux sont opaques, vous ne pouvez pas les inspecter, en fait vous ne pouvez même pas demander si un canal est fermé ou pas. néant.

La deuxième préoccupation – faire plus que le rendement superficiel signifierait une transformation complète du programme. C’est un compromis et je pense que c’est raisonnable. Le niveau de composition est que les canaux ne vont pas dans les blocs et qu’ils composent bien.

Le souci final, vous pouvez facilement faire des cartes de style Rx / filtrer / réduire les opérations sur les canaux et les gens l’ont déjà fait.

La limitation de la macro go (sa localité) est également une fonctionnalité: elle applique la localité du code source des opérations avec état.

  1. c’est un peu l’inverse, Core.async ne peut être utilisé que dans des systèmes où l’immuabilité est la norme . Les principes de Clojure activent donc core.async plutôt que l’inverse.

  2. Ceci est une limitation, se produit également ailleurs, la limitation des fonctions anonymes ne composant pas avec le symbole % semble au moins présenter la même idée. Ce n’est pas que trouver un autre cas de limitation le rend bien sûr.

  3. Je ne l’ai pas vu moi-même, même si ce serait un pas en arrière si vous essayiez de prendre un code simple et propre lorsqu’il est exprimé d’une manière, puis l’exprimez d’une manière qui n’est pas comme ça. .

Rich Hickey a déclaré dans l’une des conférences de blip.tv que Clojure était “85% fonctionnel”. J’aime voir core.async comme une partie des 15% restants. Core.async est idéal pour une interaction utilisateur solide, entre autres choses qui aurait été réalisée par des promesses, des retards et d’autres choses, probablement de manière plus désordonnée.

Chaque programme a deux parties, une partie qui est toujours des données de conversation non fonctionnelles, crache et ainsi de suite. Pour cette partie que nous connaissons, core.async, il est certain que core.async a des propriétés mutables, mais notez deux choses. L’état des canaux est géré de manière basique par la bibliothèque. Les choses que vous mettez dessus sont ce que l’on pourrait appeler flowstate. Cela signifie que lorsque vous mettez quelque chose comme un canal, vous ne vous attendez pas à y revenir, ni même à le changer.

Core.async est sympa pour gérer cette partie de votre programme. Pour le rest, toute la transformation et le calcul sur vos données, clojure fait de son mieux pour vous donner de bons outils pour le faire fonctionnellement.

Il me semble que cela empêche la simplicité et la composition. Pourquoi n’est-ce pas un problème?

Il y a deux mondes, le monde synchrone et le monde asynchrone. Vous pouvez mettre des choses, ou sortir des choses de l’async avec put! et prend! mais à part ça (et peut-être une autre fonction), ces mondes sont séparés les uns des autres. Clojure ne veut pas devenir un langage complètement asynchrone. Les fonctions et la transformation des données doivent être composables.

Peut-être en conséquence des deux problèmes précédents, beaucoup de code avec core.async utilise des constructions de niveau inférieur telles que loop / recur au lieu de map / filter / Reduce. N’est-ce pas un pas en arrière

Une telle opération sur les canaux sera possible. Core.async est encore jeune et toutes les constructions et fonctions possibles n’ont pas encore été écrites.

Mais en général, si vous avez de grandes transformations de données, vous ne voulez pas vraiment les faire dans un monde asynchrone, vous voulez les avoir dans une collection et y lancer quelque chose comme le framework reducres.

La principale chose à comprendre est la suivante: core.async n’est pas la nouvelle norme, c’est juste une autre chose qui vous aide à programmer. Parfois, vous avez besoin de STM, parfois d’agents, parfois de CSP, cela dépend et clojure veut vous donner toutes les options.

Une des raisons pour lesquelles les utilisateurs aiment core.async est parce que cela aide avec des choses qui sont autrement difficiles à gérer, comme le traitement des rappels.

peut-être une solution possible pour utiliser ( dehors de la macro peut être faite avec la macro et son temps d’extension de la macro:

Ceci est mon exemple:

 (ns fourclojure.asynco (require [clojure.core.async :as async :refer :all])) (defmacro runtime--fn [the-fn the-value] `(~the-fn ~the-value) ) (defmacro call-fn [ the-fn] `(runtime--fn ~the-fn ( 

Et pour le tester:

 (read-channel print) (repeatedly 50 paint) 

J'ai essayé cette solution dans un go nested et fonctionne également. Mais je ne suis pas sûr que cela puisse être un chemin correct