Programmer en Haskell : immersion dans la programmation fonctionnelle pure
Découvrez Haskell, le langage fonctionnel pur qui va transformer votre façon de programmer. Monades, types, évaluation paresseuse et pattern matching expliqués clairement.
Haskell : la programmation fonctionnelle pure à son apogée
Si vous êtes habitué aux langages impératifs comme Python, JavaScript ou Java, Haskell va vous bousculer. Et c'est exactement pour ça qu'il vaut la peine d'être appris. Haskell est un langage de programmation fonctionnel pur, statiquement typé, avec évaluation paresseuse. Il force à penser différemment et rend meilleur dans tous les autres langages.
Les principes fondamentaux de Haskell
1. Pureté et absence d'effets de bord
En Haskell, une fonction ne peut pas modifier l'état du programme. Elle prend des entrées et retourne une sortie, c'est tout. Cela rend le code prévisible, testable et parallélisable naturellement.
-- Fonction pure : même entrée = même sortie, toujours
carre :: Int -> Int
carre x = x * x
-- GHCi
-- > carre 5
-- 25
2. Typage statique fort avec inférence
Haskell a un système de types très expressif. Le compilateur GHC infère les types automatiquement, mais on peut les annoter explicitement :
-- Annotation de type explicite
addition :: Int -> Int -> Int
addition a b = a + b
-- Avec types polymorphes
identite :: a -> a
identite x = x
-- Le type List avec paramètre
longueur :: [a] -> Int
longueur [] = 0
longueur (_:xs) = 1 + longueur xs
3. Évaluation paresseuse (Lazy Evaluation)
Haskell n'évalue les expressions que lorsqu'elles sont nécessaires. Cela permet de travailler avec des structures infinies :
-- Liste infinie des nombres naturels
naturels :: [Int]
naturels = [1..]
-- Prendre les 10 premiers — Haskell ne calcule que ce qu'on demande
dixPremiers :: [Int]
dixPremiers = take 10 naturels
-- [1,2,3,4,5,6,7,8,9,10]
-- Suite de Fibonacci infinie
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
-- > take 10 fibs
-- [0,1,1,2,3,5,8,13,21,34]
Les fonctions d'ordre supérieur
En Haskell, les fonctions sont des citoyens de première classe. On peut les passer en argument, les retourner, les composer :
-- map : appliquer une fonction à chaque élément
doubles :: [Int] -> [Int]
doubles = map (*2)
-- filter : garder les éléments satisfaisant un prédicat
pairs :: [Int] -> [Int]
pairs = filter even
-- foldl : réduction (équivalent de reduce en JS)
somme :: [Int] -> Int
somme = foldl (+) 0
-- Composition de fonctions avec (.)
traiteListe :: [Int] -> Int
traiteListe = somme . pairs . doubles
-- > traiteListe [1..10]
-- 60
Pattern Matching : le cœur de l'élégance Haskell
-- Arbre binaire de recherche
data Arbre a = Vide | Noeud a (Arbre a) (Arbre a)
-- Insertion
inserer :: Ord a => a -> Arbre a -> Arbre a
inserer x Vide = Noeud x Vide Vide
inserer x (Noeud v gauche droit)
| x < v = Noeud v (inserer x gauche) droit
| x > v = Noeud v gauche (inserer x droit)
| otherwise = Noeud v gauche droit
-- Recherche
contient :: Ord a => a -> Arbre a -> Bool
contient _ Vide = False
contient x (Noeud v gauche droit)
| x == v = True
| x < v = contient x gauche
| otherwise = contient x droit
Les Monades : gérer les effets de bord proprement
Les monades sont souvent présentées comme difficiles, mais elles sont simplement un patron de conception pour chaîner des opérations avec contexte :
-- Maybe Monade : gérer l'absence de valeur
diviserSafe :: Int -> Int -> Maybe Int
diviserSafe _ 0 = Nothing
diviserSafe a b = Just (a `div` b)
-- Chaînage avec >>= (bind)
calcul :: Int -> Int -> Int -> Maybe Int
calcul a b c = diviserSafe a b >>= diviserSafe c
-- IO Monade : interagir avec le monde extérieur
main :: IO ()
main = do
putStrLn "Entrez votre prénom :"
prenom <- getLine
putStrLn ("Bonjour, " ++ prenom ++ " !")
Typeclasses : le polymorphisme à la Haskell
Les typeclasses sont similaires aux interfaces en POO, mais bien plus puissantes :
-- Définir une typeclass
class Affichable a where
afficher :: a -> String
-- Implémenter pour différents types
data Couleur = Rouge | Vert | Bleu
instance Affichable Couleur where
afficher Rouge = "Rouge"
afficher Vert = "Vert"
afficher Bleu = "Bleu"
instance Affichable Int where
afficher n = "Nombre : " ++ show n
-- Fonction générique qui accepte tout Affichable
imprimer :: Affichable a => a -> IO ()
imprimer x = putStrLn (afficher x)
Installer GHC et démarrer
# Installer GHCup (gestionnaire Haskell)
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
# Lancer le REPL interactif
ghci
# Compiler un fichier
ghc -o monProgramme Main.hs
# Utiliser Stack (gestionnaire de projet)
stack new monProjet
cd monProjet
stack build
stack run
Pourquoi apprendre Haskell même si vous ne l'utilisez pas en prod ?
- Vous comprendrez mieux les types génériques dans TypeScript, Java, Rust
- Vous écrirez du JavaScript plus fonctionnel (map/filter/reduce sans bugs)
- Vous appréhenderez Rust et son système de types bien plus facilement
- Vous penserez aux cas limites dès la conception (pas de null, pas d'exceptions implicites)
- Haskell est utilisé en finance, vérification formelle, compilateurs (GHC lui-même est en Haskell)
Conclusion
Haskell n'est pas un langage pour les débutants, mais c'est l'un des plus enrichissants qu'un développeur puisse étudier. Il remet en question les habitudes, force à raisonner sur les types, les effets et la composition. Une fois qu'on a compris les monades et les fonctions d'ordre supérieur en Haskell, tout le reste semble plus simple.
Commentaires approuves
Enfin un article en français sur Haskell accessible ! La partie sur les monades était la plus claire que j'ai lue. Merci beaucoup, je vais essayer GHCi ce soir.
L'exemple de la suite Fibonacci infinie avec zipWith est magnifique. C'est exactement ce genre de code qui montre la beauté de Haskell. Bravo pour l'article.
Très bon article. J'ai commencé Haskell il y a 3 mois et ça m'a clairement rendu meilleur en TypeScript. Le passage sur l'évaluation paresseuse est bien expliqué.