From 0dd3736feae6f7a05998b99d2548a2c1b23a98ea Mon Sep 17 00:00:00 2001 From: "Yann Esposito (Yogsototh)" Date: Sat, 24 Aug 2019 16:14:04 +0200 Subject: [PATCH] Added Haskell Intro from Lambda riviera --- src/css/minimalist.css | 2 +- src/css/slides.css | 3 + src/slides.org | 6 + src/slides/Intro-to-FP-with-Haskell.org | 894 ++++++++++++++++++++++++ 4 files changed, 904 insertions(+), 1 deletion(-) create mode 100644 src/slides/Intro-to-FP-with-Haskell.org diff --git a/src/css/minimalist.css b/src/css/minimalist.css index e96984e..cd1e9bc 100644 --- a/src/css/minimalist.css +++ b/src/css/minimalist.css @@ -218,7 +218,7 @@ navigation > a { margin-top: 10px; } #content,.content { - max-width: 50em; + max-width: 51em; margin: 0 1em; padding: 1px; } diff --git a/src/css/slides.css b/src/css/slides.css index 25c9cce..e79901c 100644 --- a/src/css/slides.css +++ b/src/css/slides.css @@ -12,3 +12,6 @@ margin: 1em 0; text-align: center; } +#content,.content { + max-width: 53em !important; +} diff --git a/src/slides.org b/src/slides.org index a5fcf2a..ffa87f6 100644 --- a/src/slides.org +++ b/src/slides.org @@ -14,3 +14,9 @@ about how to gain back autonomy with git by providing most features Github propose just with a few conventions. I even written a tool to help manage issues, code review, git hosting, etc... + +** French talks + +- [2018-03-15 Thu] + [[file:slides/Intro-to-FP-with-Haskell.org][Introduction à la programmation fonctionnelle avec Haskell]] + is a small French talk introducing Haskell. diff --git a/src/slides/Intro-to-FP-with-Haskell.org b/src/slides/Intro-to-FP-with-Haskell.org new file mode 100644 index 0000000..9a586a7 --- /dev/null +++ b/src/slides/Intro-to-FP-with-Haskell.org @@ -0,0 +1,894 @@ +#+Title: Introduction à la Programmation Fonctionnelle avec Haskell +#+Author: Yann Esposito +#+Email: yann@esposito.host +#+Date: <2018-03-15 Thu> +#+LANGUAGE: fr +#+LANG: fr +#+HTML_HEAD: +* Introduction à la Programmation Fonctionnelle avec Haskell +*** main :: IO () + +#+BEGIN_SRC +████████████████████████████████████████████████████████████████████████████████████ +█ █ +█ Initialiser l'env de dev █ +█ █ +████████████████████████████████████████████████████████████████████████████████████ +#+END_SRC + +Install **stack**: + +#+BEGIN_SRC bash +curl -sSL https://get.haskellstack.org/ | sh +#+END_SRC + +Install **nix**: + +#+BEGIN_SRC bash +curl https://nixos.org/nix/install | sh +#+END_SRC + +** Programmation Fonctionnelle? +*** Von Neumann Architecture (1945) + +#+BEGIN_SRC + +--------------------------------+ + | +----------------------------+ | + | | central processing unit | | + | | +------------------------+ | | + | | | Control Unit | | | ++------+ | | +------------------------+ | | +--------+ +|input +---> | +------------------------+ | +--> output | ++------+ | | | Arithmetic/Logic Unit | | | +--------+ + | | +------------------------+ | | + | +-------+---^----------------+ | + | | | | + | +-------v---+----------------+ | + | | Memory Unit | | + | +----------------------------+ | + +--------------------------------+ +#+END_SRC + +made with http://asciiflow.com + +*** Von Neumann vs Church + +- programmer à partir de la machine (Von Neumann) + + tire vers l'optimisation + + mots de bits, caches, détails de bas niveau + + actions séquentielles + + *1 siècle d'expérience* + +. . . + +- programmer comme manipulation de symbole (Alonzo Church) + + tire vers l'abstraction + + plus proche des représentations mathématiques + + ordre d'évaluation non imposé + + *4000 ans d'expérience* + +*** Histoire + +- λ-Calculus, Alonzo Church & Rosser 1936 + - Foundation, explicit side effect no implicit state + +. . . + +- LISP (McCarthy 1960) + - Garbage collection, higher order functions, dynamic typing + +. . . + +- ML (1969-80) + - Static typing, Algebraic Datatypes, Pattern matching + +. . . + +- Miranda (1986) → Haskell (1992‥) + - Lazy evaluation, pure + +** Pourquoi Haskell? +*** Simplicité par l'abstraction + + *=/!\= SIMPLICITÉ ≠ FACILITÉ =/!\=* + +- mémoire (garbage collection) +- ordre d'évaluation (non strict / lazy) +- effets de bords (pur) +- manipulation de code (referential transparency) + +. . . + +Simplicité: Probablement le meilleur indicateur de réussite de projet. + +*** Production Ready™ + +- rapide + - équivalent à Java (~ x2 du C) + - parfois plus rapide que C + - bien plus rapide que python et ruby +. . . +- communauté solide + - 3k comptes sur Haskellers + - >30k sur reddit /(35k rust, 45k go, 50k nodejs, 4k ocaml, 13k clojure)/ + - libs >12k sur hackage +. . . +- entreprises + - Facebook (fighting spam, HAXL, ...) + - beaucoup de startups, finance en général +. . . +- milieu académique + - fondations mathématiques + - fortes influences des chercheurs + - tire le langage vers le haut + +*** Tooling + +- compilateur (GHC) +- gestion de projets ; cabal, stack, hpack, etc... +- IDE / hlint ; rapidité des erreurs en cours de frappe +- frameworks hors catégorie (servant, yesod) +- ecosystèmes très matures et inovant + - Elm (⇒ frontend) + - Purescript (⇒ frontend) + - GHCJS (⇒ frontend) + - Idris (types dépendants) + - Hackett (typed LISP avec macros) + - Eta (⇒ JVM) + +*** Qualité + +#+BEGIN_QUOTE +/Si ça compile alors il probable que ça marche/ +#+END_QUOTE +. . . +- tests unitaires : + chercher quelques erreurs manuellements +. . . +- /test génératifs/ : + chercher des erreurs sur beaucoups de cas générés aléatoirements + & aide pour trouver l'erreur sur l'objet le plus simple +. . . +- /finite state machine generative testing/ : + chercher des erreurs sur le déroulement des actions + entre différents agents indépendants +. . . +- *preuves*: + chercher des erreur sur *TOUTES* les entrées possibles + possible à l'aide du système de typage + +* Premiers Pas en Haskell +*** DON'T PANIC + +#+BEGIN_SRC +██████╗ ██████╗ ███╗ ██╗████████╗ ██████╗ █████╗ ███╗ ██╗██╗ ██████╗██╗ +██╔══██╗██╔═══██╗████╗ ██║╚══██╔══╝ ██╔══██╗██╔══██╗████╗ ██║██║██╔════╝██║ +██║ ██║██║ ██║██╔██╗ ██║ ██║ ██████╔╝███████║██╔██╗ ██║██║██║ ██║ +██║ ██║██║ ██║██║╚██╗██║ ██║ ██╔═══╝ ██╔══██║██║╚██╗██║██║██║ ╚═╝ +██████╔╝╚██████╔╝██║ ╚████║ ██║ ██║ ██║ ██║██║ ╚████║██║╚██████╗██╗ +╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═════╝╚═╝ +#+END_SRC + +- Haskell peut être difficile à vraiment maîtriser +- Trois languages en un: + - Fonctionnel + - Imperatif + - Types +- Polymorphisme: + - contexte souvent semi-implicite change le comportement du code. + +*** Fichier de script isolé + +Avec Stack: https://haskellstack.org + +#+BEGIN_SRC haskell +#!/usr/bin/env stack +{- stack script + --resolver lts-12.10 + --install-ghc + --package protolude +-} +#+END_SRC + +Avec Nix: https://nixos.org/nix/ + +#+BEGIN_SRC shell +#! /usr/bin/env nix-shell +#! nix-shell -i runghc +#! nix-shell -p "ghc.withPackages (ps: [ ps.protolude ])" +#! nix-shell -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/18.09-beta.tar.gz" +#+END_SRC + +*** Hello World! (1/3) + +#+BEGIN_SRC haskell +-- hello.hs +main :: IO () +main = putStrLn "Hello World!" +#+END_SRC + +#+BEGIN_SRC +> chmod +x hello.hs +> ./hello.hs +Hello World! +#+END_SRC + +#+BEGIN_SRC +> stack ghc -- hello.hs +> ./hello +Hello World! +#+END_SRC + +*** Hello World! (2/3) + +#+BEGIN_SRC haskell +main :: IO () +main = putStrLn "Hello World!" +#+END_SRC + +- ~::~ de type ; +- le type de ~main~ est ~IO ()~. +- ~=~ égalité (la vrai, on peut interchanger ce qu'il y a des deux cotés) ; +- le type de ~putStrLn~ est ~String -> IO ()~ ; +- application de fonction =f x= pas =f(x)=, pas de parenthèse nécessaire ; + +*** Hello World! (3/3) + +#+BEGIN_SRC haskell +main :: IO () +main = putStrLn "Hello World!" +#+END_SRC + +- Le type ~IO a~ signifie: C'est une description d'une procédure qui quand elle + est évaluée peut faire des actions d'IO qui retournera une valeur de type ~a~ ; +- ~main~ est le nom du point d'entrée du programme ; +- Haskell runtime va chercher pour ~main~ et l'exécute. + +** What is your name? +*** What is your name? (1/2) + +#+BEGIN_SRC haskell +main :: IO () +main = do + putStrLn "Hello! What is your name?" + name <- getLine + let output = "Nice to meet you, " ++ name ++ "!" + putStrLn output +#+END_SRC + +. . . + +- l'indentation est importante ! +- ~do~ commence une syntaxe spéciale qui permet de séquencer des actions ~IO~ ; +- le type de ~getLine~ est ~IO String~ ; +- ~IO String~ signifie: Ceci est la description d'une procédure qui lorsqu'elle + est évaluée peut faire des actions IO et retourne une valeur de type ~String~. + +*** What is your name? (2/2) + +#+BEGIN_SRC haskell +main :: IO () +main = do + putStrLn "Hello! What is your name?" + name <- getLine + let output = "Nice to meet you, " ++ name ++ "!" + putStrLn output +#+END_SRC + +- le type de ~getLine~ est ~IO String~ +- le type de ~name~ est ~String~ +- ~<-~ est une syntaxe spéciale qui n'apparait que dans la notation ~do~ +- ~<-~ signifie: évalue la procédure et attache la valeur renvoyée dans le nom + à gauche de ~<-~ +- ~let = ~ signifie que ~name~ est interchangeable avec ~expr~ pour + le reste du bloc ~do~. +- dans un bloc ~do~, ~let~ n'a pas besoin d'être accompagné par ~in~ à la fin. + +** Erreurs classiques +*** Erreur classique #1 + +#+BEGIN_SRC haskell +main :: IO () +main = do + putStrLn "Hello! What is your name?" + let output = "Nice to meet you, " ++ getLine ++ "!" + putStrLn output +#+END_SRC + +#+BEGIN_SRC +/Users/yaesposi/.deft/pres-haskell/name.hs:6:40: warning: [-Wdeferred-type-errors] + • Couldn't match expected type ‘[Char]’ + with actual type ‘IO String’ + • In the first argument of ‘(++)’, namely ‘getLine’ + In the second argument of ‘(++)’, namely ‘getLine ++ "!"’ + In the expression: "Nice to meet you, " ++ getLine ++ "!" + | +6 | let output = "Nice to meet you, " ++ getLine ++ "!" + | ^^^^^^^ +Ok, one module loaded. +#+END_SRC + +*** Erreur classique #1 + +- ~String~ est ~[Char]~ +- Haskell n'arrive pas à faire matcher le type ~String~ avec ~IO String~. +- ~IO a~ et ~a~ sont différents + +*** Erreur classique #2 + +#+BEGIN_SRC haskell +main :: IO () +main = do + putStrLn "Hello! What is your name?" + name <- getLine + putStrLn "Nice to meet you, " ++ name ++ "!" +#+END_SRC + +#+BEGIN_SRC +/Users/yaesposi/.deft/pres-haskell/name.hs:7:3: warning: [-Wdeferred-type-errors] + • Couldn't match expected type ‘[Char]’ with actual type ‘IO ()’ + • In the first argument of ‘(++)’, namely + ‘putStrLn "Nice to meet you, "’ + In a stmt of a 'do' block: + putStrLn "Nice to meet you, " ++ name ++ "!" + In the expression: + do putStrLn "Hello! What is your name?" + name <- getLine + putStrLn "Nice to meet you, " ++ name ++ "!" + | +7 | putStrLn "Nice to meet you, " ++ name ++ "!" +#+END_SRC + +*** Erreur classique #2 (fix) + +- Des parenthèses sont nécessaires +- L'application de fonction se fait de gauche à droite + +#+BEGIN_SRC haskell +main :: IO () +main = do + putStrLn "Hello! What is your name?" + name <- getLine + putStrLn ("Nice to meet you, " ++ name ++ "!") +#+END_SRC + +* Concepts avec exemples +*** Concepts +- /style déclaratif & récursif/ +- /immutabilité/ +- /pureté/ (par défaut) +- /evaluation paraisseuse/ (par défaut) +- /ADT & typage polymorphique/ + +*** /Style déclaratif & récursif/ + +#+BEGIN_SRC python +>>> x=0 +... for i in range(1,11): +... tmp = i*i +... if tmp%2 == 0: +... x += tmp +>>> x +220 +#+END_SRC + +#+BEGIN_SRC haskell +-- (.) composition (de droite à gauche) +Prelude> sum . filter even . map (^2) $ [1..10] +220 +Prelude> :set -XNoImplicitPrelude +Prelude> import Protolude +-- (&) flipped fn application (de gauche à droite) +Protolude> [1..10] & map (^2) & filter even & sum +220 +#+END_SRC + +*** /Style déclaratif & récursif/ + +#+BEGIN_SRC python +>>> x=0 +... for i in range(1,11): +... j = i*3 +... tmp = j*j +... if tmp%2 == 0: +... x += tmp +#+END_SRC + +#+BEGIN_SRC haskell +Prelude> sum . filter even . map (^2) . map (*3) $ [1..10] +Protolude> [1..10] & map (*3) & map (^2) & filter even & sum +#+END_SRC + +*** /Style déclaratif & récursif/ + +- Contrairement aux languages impératifs la récursion n'est généralement pas chère. +- tail recursive function, mais aussi à l'aide de la lazyness + +*** /Imutabilité/ + +#+BEGIN_SRC haskell +-- | explicit recursivity +incrementAllEvenNumbers :: [Int] -> [Int] +incrementAllEvenNumbers (x:xs) = y:incrementAllEvenNumbers xs + where y = if even x then x+1 else x + +-- | better with use of higher order functions +incrementAllEvenNumbers' :: [Int] -> [Int] +incrementAllEvenNumbers' ls = map incrementIfEven ls + where + incrementIfEven :: Int -> Int + incrementIfEven x = if even x then x+1 else x +#+END_SRC + +*** /Pureté/: Function vs Procedure/Subroutines + +- Une /fonction/ n'a pas d'effet de bord +- Une /Procedure/ ou /subroutine/ but engendrer des effets de bords lors de son + évaluation + +*** /Pureté/: Function vs Procedure/Subroutines (exemple) + +#+BEGIN_SRC haskell +dist :: Double -> Double -> Double +dist x y = sqrt (x**2 + y**2) +#+END_SRC + + +#+BEGIN_SRC haskell +getName :: IO String +getName = readLine +#+END_SRC + +- *IO a* ⇒ *IMPUR* ; effets de bords hors evaluation : + - lire un fichier ; + - écrire sur le terminal ; + - changer la valeur d'une variable en RAM est impur. + +*** /Pureté/: Gain, paralellisation gratuite + +#+BEGIN_SRC haskell +import Foreign.Lib (f) +-- f :: Int -> Int +-- f = ??? + +foo = sum results + where results = map f [1..100] +#+END_SRC + +. . . + +*~pmap~ FTW!!!!! Assurance d'avoir le même résultat avec 32 cœurs* + +#+BEGIN_SRC haskell +import Foreign.Lib (f) +-- f :: Int -> Int +-- f = ??? + +foo = sum results + where results = pmap f [1..100] +#+END_SRC + +*** /Pureté/: Structures de données immuable + +Purely functional data structures, +/Chris Okasaki/ + +Thèse en 1996, et un livre. + +Opérations sur les listes, tableaux, arbres +de complexité amortie equivalent ou proche +(pire des cas facteur log(n)) +de celle des structures de données muables. + +*** /Évaluation parraisseuse/: Stratégies d'évaluations + +=(h (f a) (g b))= peut s'évaluer: + +- =a= → =(f a)= → =b= → =(g b)= → =(h (f a) (g b))= +- =b= → =a= → =(g b)= → =(f a)= → =(h (f a) (g b))= +- =a= et =b= en parallèle puis =(f a)= et =(g b)= en parallèle et finallement + =(h (f a) (g b))= +- =h= → =(f a)= seulement si nécessaire et puis =(g b)= seulement si nécessaire + +Par exemple: =(def h (λx.λy.(+ x x)))= il n'est pas nécessaire d'évaluer =y=, +dans notre cas =(g b)= + +*** /Évaluation parraisseuse/: Exemple + +#+BEGIN_SRC haskell +quickSort [] = [] +quickSort (x:xs) = quickSort (filter (=x) xs) + +minimum list = head (quickSort list) +#+END_SRC + +Un appel à ~minimum longList~ ne vas pas ordonner toute la liste. +Le travail s'arrêtera dès que le premier élément de la liste ordonnée sera trouvé. + +~take k (quickSort list)~ est en ~O(n + k log k)~ où ~n = length list~. +Alors qu'avec une évaluation stricte: ~O(n log n)~. + +*** /Évaluation parraisseuse/: Structures de données infinies (zip) + +#+BEGIN_SRC haskell +zip :: [a] -> [b] -> [(a,b)] +zip [] _ = [] +zip _ [] = [] +zip (x:xs) (y:ys) = (x,y):zip xs ys +#+END_SRC + +#+BEGIN_SRC haskell +zip [1..] ['a','b','c'] +#+END_SRC + +s'arrête et renvoie : + +#+BEGIN_SRC haskell +[(1,'a'), (2,'b'), (3, 'c')] +#+END_SRC + +*** /Évaluation parraisseuse/: Structures de données infinies (2) + +#+BEGIN_SRC haskell +Prelude> zipWith (+) [0,1,2,3] [10,100,1000] +[10,101,1002] +Prelude> take 3 [1,2,3,4,5,6,7,8,9] +[1,2,3] +#+END_SRC + +#+BEGIN_SRC haskell +Prelude> fib = 0:1:(zipWith (+) fib (tail fib)) +Prelude> take 10 fib +[0,1,1,2,3,5,8,13,21,34] +#+END_SRC + +*** /ADT & Typage polymorphique/ + +Algebraic Data Types. + +#+BEGIN_SRC haskell +data Void = Void Void -- 0 valeur possible! +data Unit = () -- 1 seule valeur possible + +data Product x y = P x y +data Sum x y = S1 x | S2 y +#+END_SRC + +Soit ~#x~ le nombre de valeurs possibles pour le type ~x~ +alors: + +- ~#(Product x y) = #x * #y~ +- ~#(Sum x y) = #x + #y~ + +*** /ADT & Typage polymorphique/: Inférence de type + +À partir de : + +#+BEGIN_SRC haskell +zip [] _ = [] +zip _ [] = [] +zip (x:xs) (y:ys) = (x,y):zip xs ys +#+END_SRC + +le compilateur peut déduire: + +#+BEGIN_SRC haskell +zip :: [a] -> [b] -> [(a,b)] +#+END_SRC + +** Composabilité +*** Composabilité vs Modularité + +Modularité: soit un ~a~ et un ~b~, je peux faire un ~c~. + ex: x un graphique, y une barre de menu => une page + ~let page = mkPage ( graphique, menu )~ + +Composabilité: soit deux ~a~ je peux faire un autre ~a~. + ex: x un widget, y un widget => un widget + ~let page = x <+> y~ + +Gain d'abstraction, moindre coût. + +*Hypothèses fortes sur les ~a~* + +*** Exemples + +- *Semi-groupes* 〈+〉 +- *Monoides* 〈0,+〉 + +- *Catégories* 〈obj(C),hom(C),∘〉 +- Foncteurs ~fmap~ (~(<$>)~) +- Foncteurs Applicatifs ~ap~ (~(<*>)~) +- Monades ~join~ +- Traversables ~map~ +- Foldables ~reduce~ + +* Catégories de bugs évités avec Haskell + +*** Real Productions Bugs™ + +Bug vu des dizaines de fois en prod malgré: + + 1. specifications fonctionnelles + 2. spécifications techniques + 3. tests unitaires + 4. 3 envs, dev, recette/staging/pre-prod, prod + 5. Équipe de QA qui teste en recette + +Solutions simples. + +*** **Null Pointer Exception**: Erreur classique (1) + +Au début du projet : + +#+BEGIN_SRC javascript +int foo( x ) { + return x + 1; +} +#+END_SRC + +*** **Null Pointer Exception**: Erreur classique (2) + +Après quelques semaines/mois/années : + +#+BEGIN_SRC javascript +import do_shit_1 from "foreign-module"; +int foo( x ) { + ... + var y = do_shit_1(x); + ... + return do_shit_20(y) +} +... +var val = foo(26/2334 - Math.sqrt(2)); +#+END_SRC + +. . . + +#+BEGIN_SRC +███████ █████ ███ ███ ███ ███ ███ ███ ███ ███ ███ +███ ██ ███ ███ ███ ███ ████ ████ ███ ███ ███ ███ ███ +███ ██ ███ ███ ███ ███ █████ █████ ███ ███ ███ ███ ███ +███████ ███ ███ ███ ███ ███ █████ ███ ███ ███ ███ ███ ███ +███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ +███ ███ ███ ███ ███ ███ ███ █ ███ █ █ █ █ █ +███ ███ ███ ███ ███ ███ ███ ███ +███████ █████ █████ ███ ███ ███ ███ ███ ███ ███ +#+END_SRC + +| *Null Pointer Exception* + +*** Null Pointer Exception: Data type ~Maybe~ + +#+BEGIN_SRC haskell +data Maybe a = Just a | Nothing +... +foo :: Maybe a +... +myFunc x = let t = foo x in + case t of + Just someValue -> doThingsWith someValue + Nothing -> doThingWhenNothingIsReturned +#+END_SRC + +Le compilateur oblige à tenir compte des cas particuliers! +Impossible d'oublier. + +*** Null Pointer Exception: Etat + +- Rendre impossibe de fabriquer un état qui devrait être impossible d'avoir. +- Pour aller plus loin voir, FRP, CQRS/ES, Elm-architecture, etc... + +*** Erreur due à une typo + +#+BEGIN_SRC haskell +data Foo x = LongNameWithPossibleError x +... +foo (LongNameWithPosibleError x) = ... +#+END_SRC + +*Erreur à la compilation*: +Le nom d'un champ n'est pas une string +(voir les objets JSON). + +*** Echange de parameters + +#+BEGIN_SRC haskell +data Personne = Personne { uid :: Int, age :: Int } +foo :: Int -> Int -> Personne -- ??? uid ou age? +#+END_SRC + +#+BEGIN_SRC haskell +newtype UID = UID Int deriving (Eq) +data Personne = Personne { uid :: UID, age :: Int } +foo :: UDI -> Int -> Personne -- Impossible de confondre +#+END_SRC + +*** Changement intempestif d'un Etat Global + +#+BEGIN_SRC haskell +foo :: GlobalState -> x +#+END_SRC + +*~foo~ ne peut pas changer =GlobalState=* + +* Organisation du Code +*** Grands Concepts + +Procedure vs Functions: + +| Gestion d'une configuration globale | +| Gestion d'un état global | +| Gestion des Erreurs | +| Gestion des IO | + +*** Monades + +Pour chacun de ces /problèmes/ il existe une monade: + +| Gestion d'une configuration globale | ~Reader~ | +| Gestion d'un état global | ~State~ | +| Gestion des Erreurs | ~Either~ | +| Gestion des IO | ~IO~ | + +*** Effets + +Gestion de plusieurs Effets dans la même fonction: + +- MTL +- Free Monad +- Freer Monad + +Idée: donner à certaines sous-fonction accès à une partie des effets seulement. + +Par exemple: +- limiter une fonction à la lecture de la DB mais pas l'écriture. +- limiter l'écriture à une seule table +- interdire l'écriture de logs +- interdire l'écriture sur le disque dur +- etc... + +*** Exemple dans un code réel (1) + +#+BEGIN_SRC haskell +-- | ConsumerBot type, the main monad in which the bot code is written with. +-- Provide config, state, logs and IO +type ConsumerBot m a = + ( MonadState ConsumerState m + , MonadReader ConsumerConf m + , MonadLog (WithSeverity Doc) m + , MonadBaseControl IO m + , MonadSleep m + , MonadPubSub m + , MonadIO m + ) => m a +#+END_SRC + +*** Exemple dans un code réel (2) + +#+BEGIN_SRC haskell +bot :: Manager + -> RotatingLog + -> Chan RedditComment + -> TVar RedbotConfs + -> Severity + -> IO () +bot manager rotLog pubsub redbots minSeverity = do + TC.setDefaultPersist TC.filePersist + let conf = ConsumerConf + { rhconf = RedditHttpConf { _connMgr = manager } + , commentStream = pubsub + } + void $ autobot + & flip runReaderT conf + & flip runStateT (initState redbots) + & flip runLoggingT (renderLog minSeverity rotLog) +#+END_SRC + + +** Règles *pragmatiques* + +*** Organisation en fonction de la complexité + +#+BEGIN_QUOTE +Make it work, make it right, make it fast +#+END_QUOTE + +- Simple: directement IO (YOLO!) +- Medium: Haskell Design Patterns: The Handle Pattern: + https://jaspervdj.be/posts/2018-03-08-handle-pattern.html +- Medium (bis): MTL / Free / Freeer / Effects... +- Gros: Three Layer Haskell Cake: + http://www.parsonsmatt.org/2018/03/22/three_layer_haskell_cake.html + + Layer 1: Imperatif + + Orienté Objet (Level 2 / 2') + + Fonctionnel + +*** 3 couches + +- *Imperatif*: + ReaderT IO + + Insérer l'état dans une ~TVar~, ~MVar~ ou ~IORef~ (concurrence) +- *Orienté Objet*: + + Handle / MTL / Free... + + donner des access ~UserDB~, ~AccessTime~, ~APIHTTP~... +- *Fonctionnel*: Business Logic ~f : Handlers -> Inputs -> Command~ + +*** Services / Lib + +Service: ~init~ / ~start~ / ~close~ + methodes... +Lib: methodes sans état interne. + +* Conclusion +*** Pourquoi Haskell? + +- avantage compétitif: qualité & productivité hors norme +- change son approche de la programmation +- les concepts appris sont utilisables dans tous les languages +- permet d'aller là où aucun autre langage ne peut vous amener +- Approfondissement sans fin: + - Théorie: théorie des catégories, théorie des types homotopiques, etc... + - Optim: compilateur + - Qualité: tests, preuves + - Organisation: capacité de contraindre de très haut vers très bas + +*** Avantage compétitif + +- France, Europe du sud & Functional Programming +- Coût Maintenance >> production d'un nouveau produit +- Coût de la refactorisation +- "Make it work, Make it right, Make it fast" moins cher. + +*** Pour la suite + +A chacun de choisir, livres, tutoriels, videos, chat, etc... + +- Voici une liste de resources : https://www.haskell.org/documentation +- Mon tuto rapide : [[http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way/][Haskell the Hard Way]] +- Moteurs de recherche par type : [[http://hayoo.fh-wedel.de][hayoo]] & [[http://haskell.org/hoogle][hoogle]] +- Communauté & News : http://haskell.org/news & ~#haskell-fr~ sur freenode +- Libs: https://hackage.haskell.org & https://stackage.org + +* Appendix +*** STM: Exemple (Concurrence) (1/2) + +#+BEGIN_SRC java +class Account { + float balance; + synchronized void deposit(float amount){ + balance += amount; } + synchronized void withdraw(float amount){ + if (balance < amount) throw new OutOfMoneyError(); + balance -= amount; } + synchronized void transfert(Account other, float amount){ + other.withdraw(amount); + this.deposit(amount); } +} +#+END_SRC + +Situation d'interblocage typique. (A transfert vers B et B vers A). + +*** STM: Exemple (Concurrence) (2/2) + +#+BEGIN_SRC haskell +deposit :: TVar Int -> Int -> STM () +deposit acc n = do + bal <- readTVar acc + writeTVar acc (bal + n) +withdraw :: TVar Int -> Int -> STM () +withdraw acc n = do + bal <- readTVar acc + if bal < n then retry + writeTVar acc (bal - n) +transfer :: TVar Int -> TVar Int -> Int -> STM () +transfer from to n = do + withdraw from n + deposit to n +#+END_SRC + +- pas de lock explicite, composition naturelle dans ~transfer~. +- si une des deux opération échoue toute la transaction échoue +- le système de type force cette opération a être atomique: + ~atomically :: STM a -> IO a~