deft/journal/2021-01-03--12-08-46Z--voeux_2021_alexandre.org

137 lines
5.4 KiB
Org Mode
Raw Normal View History

#+TITLE: voeux 2021 alexandre
#+Author: Yann Esposito
#+Date: [2021-01-03]
- tags ::
- source ::
Bonjour Alexandre, je te souhaite à toi et toute ta famille une très bonne
année 2021 !
Je vous souhaite à tous une très bonne santé surtout dans le climat actuel.
De trouver un bon équilibre entre la vie familiale les passions et le
travail.
J'en profite aussi pour parler d'autres choses techniques.
Je n'écris plus vraiment de nouvel article sur Haskell/Purescript.
Cependant, j'ai passé pas mal de temps à réfléchir à comment architecturer
du code à la fois dans Haskell/Purescript et Clojure.
Et j'ai l'impression qu'une solution qu'on utilise actuellement en Clojure
bien qu'à première vue très inférieure aux solutions proposées par
Haskell/Purescript sont en fait supérieure lorsqu'on les regardes selon le
bon angle.
L'idée de fond c'est que les systèmes d'effets/Free Monad (et les
sous-systèmes, style MTL, handler pattern) sont en fait une sorte de sous
ensemble de ce que l'on peut obtenir avec un système de "Services".
Si on regarde l'intérêt de ces système d'organisation de code --
le plus évolué étant les Free Monads et les systèmes d'effets --
leur objectif est toujours plus ou moins le même.
Changer le comportement du même bloc de code en fonction du contexte
d'exécution.
En gros si on prend une function qui ressemble à:
#+begin_src haskell
my_function arg1 ... argn = do
x1 <- action1 arg1 .. argn
x2 <- action2 arg1 .. argn x1
...
z <- actionK arg1 .. argn x1 .. x<K-1>
return z
#+end_src
les =action1= à =actionK= auront des comportement différents en fonction du
contexte dans lequel il va être initialisé.
Dans le cas de Haskell, on fait au mieux pour déterminer le contexte à
compile time en utilisant les types.
Du genre:
#+begin_src haskell
main = do
effect1 <- initEffect1
effect2 <- initEffect2 effect1
...
effectM <- initEffectM effect1 ... effect<M-1>
runWithEffects [effect1,effect2,...,effectM] my_function
#+end_src
Ce système présente ainsi beaucoup d'avantanges:
1. mettre toute la business logic dans la fonction =my_function= en se
débarrassant des détails techniques au mieu en les envoyant dans les
"Effets".
2. Pouvoir facilement changer les Effects par des Effects purs et pouvoir
ainsi écrire des tests déterministes et reproductibles aisément.
3. Controller "par le haut" les sous-effets disponibles dans certaines
branches du code. On peut du coups facilement contraindre et voir que
certaines fonction ne pourront jamais accéder à la DB par exemple.
Par contre ce système à un coût sur les performances.
Je pense que le coût est négligeable dans la majorité des cas et qu'il
s'agit d'"Haskell circle jerking" lorsque les gens refusent d'utiliser
des systèmes d'effects pour cette raison.
Si on va au bout de l'utilisation de tel système en réalité on voit qu'il
faut plutôt qu'utiliser des systèmes tout prêt à l'emploi se créer beaucoup
d'effects maison pour en tirer le meilleur parti.
Typiquement on va avoir des effects avec leur pendant pur qui devraient
probablement être standards comme un effect de Log, d'accès complet à une
DB peut-être ?
Mais assez vite, on a envie de se fabriquer des Effets spécialisé au
domaine de son application.
Typiquement, si on fait de la gestion d'utilisateur on va créer un effet
pour la gestion d'utilisateur plutôt que filer un Effet complet d'accès à
la DB.
Ce qui permet de s'assurer que des fonctions n'auront accès qu'aux tables
de la DB utilisées pour les utilisateurs et pas pour les tables destinées à
d'autres buts etc...
Et c'est là qu'apparaît la première limitation des systèmes d'Effets.
Les effets ont des dépendances entre eux.
L'effet =UserEffect= doit dépendre de l'effect =DBEffect=.
Mais probablement aussi de l'effet qui permet d'écrire des logs, et de pas
mal d'autres.
Et c'est à la lumière de voir ces effects comme des outils d'application
pragmatiques et pas juste des "Effect algébriques" que l'on voit apparaître
un besoin de gestion de ces dépendances.
Un aspect important devient donc la gestion de l'ordre d'init et de stop de
tous ces effets.
En effet, pour faire =initUserEffect= il faudra lui passer en paramètre des
effets dejè instanciés (et donc initialisés) pour les logs et la DB.
Un premier problème est donc que changer/modifier des dépendances entre
effet dans un point du système revient à imposer une modification manuelle
du code dans tous les effets dépendants.
Mais aussi en particulier le code de l'init du système devient lourd à
gerrer alors qu'il existe une solution simple.
Je pense que Tardis pourrait être utile dans ce cas, mais, ça ne me semble
pas encore assez facile.
Même avec tardis, il me semble qu'il faut savoir si un effet doit arriver
avant ou après.
Là on souhaiterai pouvoir utiliser la lazyness pour une gestion
automatique des dépendances.
En réalité l'API la plus souhaitable serait de laisser la gestion de
l'ordre des dépendance et leur appel laissé à un système automatique.
Ainsi au lieu d'avoir
#+begin_src haskell
main = do
confEffect <- initConfEffect "config.dhall"
dbEffect <- initDBEffect confEffect
userEffect <- initUserEffect dbEffect confEffect
runWithEffects [confEffect,dbEffect,userEffect] myFunction
#+end_src
Il semble préférable d'écrire:
#+begin_src haskell
main = do
let effects = [ ConfEff, DBEff, UserEff ]
runWithEffects effects myFunction
#+end_src