Added Haskell Intro from Lambda riviera

This commit is contained in:
Yann Esposito (Yogsototh) 2019-08-24 16:14:04 +02:00
parent 68d993c1bf
commit 0dd3736fea
Signed by untrusted user who does not match committer: yogsototh
GPG Key ID: 7B19A4C650D59646
4 changed files with 904 additions and 1 deletions

View File

@ -218,7 +218,7 @@ navigation > a {
margin-top: 10px;
}
#content,.content {
max-width: 50em;
max-width: 51em;
margin: 0 1em;
padding: 1px;
}

View File

@ -12,3 +12,6 @@
margin: 1em 0;
text-align: center;
}
#content,.content {
max-width: 53em !important;
}

View File

@ -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.

View File

@ -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: <link rel='stylesheet' type='text/css' href='/css/slides.css' />
* 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 <name> = <expr>~ 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)
++ [x]
++ 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)~~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~