8 ans de Haskell en production, puis 8 mois d’OCaml (2023)
(chshersh.com)- Un développeur compare le ressenti concret de développement entre deux langages fonctionnels à partir de son expérience de 8 ans de Haskell et 8 mois d’OCaml en production
- Haskell offre une syntaxe plus concise et des fonctionnalités de typage puissantes, mais l’abondance de choix peut facilement détourner l’attention vers la conception et l’abstraction
- OCaml, malgré un nombre de fonctionnalités plus limité, est jugé plus propice à la concentration sur l’implémentation grâce aux modules de première classe, à une mutabilité pragmatique et à un style de code prévisible
- L’écosystème de Haskell est plus vaste, mais le choix des bibliothèques peut donner l’impression d’être une compétence à part entière, tandis qu’OCaml, bien que plus petit, propose des outils nécessaires qui fonctionnent mieux qu’on ne l’imagine
- Les deux langages restent plus petits que les langages dominants et leurs bibliothèques standard sont proches du strict minimum, mais ils sont suffisamment solides pour développer des applications industrielles si l’on ne dépend pas fortement d’un SDK spécifique
Point de départ de la comparaison
- Le point de référence est une expérience de 8 ans avec Haskell et 8 mois avec OCaml en production
- Les axes de comparaison sont la syntaxe, les fonctionnalités du langage, l’écosystème, les outils, les messages du compilateur et la bibliothèque standard
- Les deux langages ont suffisamment mûri pour répondre à de vrais besoins industriels, mais la préférence actuelle penche plutôt du côté d’OCaml
Syntaxe : Haskell est plus concis, mais OCaml garde les atouts de la famille ML
- Haskell permet d’exprimer des idées avec très peu de caractères, ce qui lui donne une forte élégance syntaxique
- OCaml, lui aussi excellent en tant que langage de la famille ML, reste un peu moins orienté vers le style implicite (tacit) que Haskell
- Dans un exemple de somme de nombres contenus dans une chaîne, Haskell l’écrit brièvement avec
sum . map read . words, tandis qu’OCaml explicite, via un pipeline, les étapes de découpage, transformation et réduction de la chaîne - La définition d’un type d’arbre binaire exprime naturellement, dans les deux langages, les types de données algébriques
- Les exemples de parsing utilisent dans les deux cas le pattern matching et des valeurs optionnelles, mais Haskell recourt à la notation
doet àguard, tandis qu’OCaml utiliseOption.bindet unmatchexplicite
Fonctionnalités : Haskell est riche, OCaml est moins dispersant
- Haskell a énormément de fonctionnalités, au point que la comparaison avec C++ peut sembler pertinente
- Cette richesse donne des outils puissants pour résoudre finement des problèmes, mais pousse aussi à réfléchir à la manière de concevoir la solution avant même de l’implémenter
- En Haskell, il est facile de se perdre dans des choix de conception entre
TypeFamilies,DataKindsouGADTs - Dans un projet OCaml existant, les mauvaises surprises se limitent souvent à des noms de variables peu parlants, un manque de documentation ou des fonctions de plus de 200 lignes, ce qui reste jugé gérable
- À l’inverse, un projet Haskell existant peut révéler une complexité difficile à anticiper même après 8 ans d’expérience
- Cette différence donne le sentiment d’être plus productif en OCaml
-
Fonctionnalités partagées par les deux langages
- Les deux proposent une syntaxe centrée sur les expressions, l’immuabilité par défaut, les fonctions d’ordre supérieur, les fonctions anonymes, les types de données algébriques et le pattern matching
- Le polymorphisme paramétrique, l’inférence de types, le sucre syntaxique pour les monades, le garbage collector, le multithreading et les GADTs sont également disponibles des deux côtés
-
Fonctionnalités plus marquantes côté Haskell
- Haskell propose la pureté par défaut, l’évaluation paresseuse composable, les type classes, les types de kind supérieur et les extensions de langage optionnelles
- Avec autant de mécanismes puissants, la manière d’abstraire peut varier fortement d’un projet à l’autre
-
Fonctionnalités plus marquantes côté OCaml
- OCaml se distingue par les modules de première classe (first-class modules), les variantes polymorphes, les objets, les classes et l’héritage, ainsi qu’une mutabilité facile à utiliser
- Son éventail de fonctionnalités est plus restreint que celui de Haskell, mais cela rend aussi les bases de code plus prévisibles
Écosystème : Haskell est plus vaste, OCaml a les solutions nécessaires
- Les deux langages restent des langages fonctionnels de niche, et il est difficile d’espérer une prise en charge de premier ordre dans les frameworks les plus récents
- Malgré cela, il existe des solutions pour la plupart des besoins courants, même s’il faut parfois écrire davantage de bindings maison
- En OCaml aussi, on peut trouver des packages utiles en pratique, par exemple :
- otoml : parseur TOML
- Mint Tea : framework TUI
- ocaml-opentelemetry : instrumentation OpenTelemetry
- awsm : client AWS pour OCaml
- petrol : API SQL rapide pour OCaml
- L’écosystème Haskell offre davantage de packages et de solutions prêtes à l’emploi
- Pour la Stripe API, Haskell dispose de 13 bibliothèques clientes, contre 1 pour OCaml, et cette dernière n’ayant pas été modifiée depuis 8 ans, cela revient pratiquement à 0
- En Haskell, même après avoir trouvé une solution, il faut encore déterminer quelle bibliothèque choisir parmi trop d’options
- Le choix des bibliothèques est parfois traité comme une compétence à part entière, au point qu’il existe des billets détaillant comment évaluer des bibliothèques
- Une nouvelle bibliothèque Haskell naît souvent non pour résoudre un autre problème, mais pour réécrire différemment avec une autre abstraction ou une nouvelle fonctionnalité
- Concevoir un logger avec des comonades peut sembler plus intéressant que de créer un client GitHub API ou de parser beaucoup de JSON
Outils : Haskell est puissant mais irrégulier, OCaml fonctionne bien
- Les outils Haskell combinent de vraies forces et des problèmes d’utilisabilité
- Hoogle est un outil très puissant, capable de rechercher dans tout l’écosystème à partir de seules signatures de types
- En contrepartie, on rencontre des problèmes comme des messages d’erreur d’outils de build, l’impossibilité de retrouver un plan de build dans un projet pourtant fonctionnel, des recompilations de l’IDE après changement de package ou l’absence de documentation de la bibliothèque standard pour certaines versions précises
- L’expérience des outils Haskell oscille entre l’étonnement face à la manière dont les autres langages peuvent vivre sans ce type d’outils, et l’étonnement inverse face à la manière dont les utilisateurs de Haskell supportent l’absence de certaines bases d’utilisabilité
- OCaml, avec son écosystème plus petit, surprend souvent par le fait que les choses marchent réellement
- Le plugin OCaml pour VSCode, basé sur le LSP, a fonctionné sans réglage particulier et sans problème
- Même si l’entrée dans les outils OCaml n’est pas la plus fluide qui soit, l’ensemble reste intuitif, robuste et fonctionne correctement dans la plupart des cas
-
Comparaison des outils
- Compilateur : OCaml utilise ocaml, Haskell utilise ghc
- REPL : OCaml utilise utop, Haskell utilise ghci
- Outils de build : OCaml utilise dune, Haskell utilise cabal et stack
- Gestionnaire de packages et dépôt : OCaml utilise opam, Haskell utilise cabal et Hackage
- Linter : OCaml utilise zanuda, Haskell utilise hlint
- Formateurs : OCaml utilise ocamlformat et topiary, Haskell utilise fourmolu, stylish-haskell, hindent, ormolu
- Recherche par type et recherche de code : OCaml utilise Sherlodoc et Sherlocode, Haskell utilise Hoogle et Hackage Search
- Playground en ligne et LSP : OCaml utilise TryOCaml et ocaml-lsp, Haskell utilise Haskell Playground et HLS
Messages du compilateur : Haskell est verbeux, OCaml est concis
- Dans un langage fonctionnel, le compilateur est l’outil central pour comprendre pourquoi le code ne satisfait pas les hypothèses prévues
- Les messages d’erreur doivent donc présenter les informations nécessaires d’une manière facile à parcourir
- Les messages du compilateur Haskell sont riches en contexte et verbeux, avec une tendance à inclure des informations redondantes ou dispersées
- Les messages du compilateur OCaml sont assez concis, parfois même trop concis
- Le programme d’exemple erroné tente d’additionner un entier et une liste, avec
x = 1 + [3, 1, 2]en Haskell etlet x = 1 + [3; 1; 2]en OCaml
Bibliothèque standard : minimale dans les deux cas, mais la qualité de la documentation distingue Haskell
- La bibliothèque standard façonne à la fois le premier programme écrit dans un langage et l’expérience d’usage qui suit
- Une bonne bibliothèque standard constitue le socle du succès d’un langage de programmation, tandis qu’une bibliothèque insuffisante entretient sans fin les débats sur des bibliothèques standard alternatives
- L’idéal serait une bibliothèque standard proche de l’approche batteries-included
- On voudrait notamment y trouver un type proche d’Option, des chaînes UTF-8,
MapetHashMap, des parseurs JSON et XML, ainsi que des primitives asynchrones - Sans cela, on doit apprendre davantage sur les outils de build et l’implémentation du suivi des dépendances qu’on ne le souhaiterait
- Build Systems a la Carte analyse précisément le domaine des outils de build et des suivis de dépendances
- Haskell comme OCaml disposent tous deux d’une bibliothèque standard relativement minimale
- Haskell n’inclut ni
MapniHashMap - OCaml n’inclut ni non-empty lists ni
Bitraversable
- Haskell n’inclut ni
- La bibliothèque standard de Haskell est
base, tandis qu’OCaml utilise la bibliothèque standard d’OCaml - La qualité de la documentation Haskell est parfois suffisamment bonne pour surprendre même des développeurs expérimentés
- La documentation Haskell permet notamment de naviguer jusqu’au code source, et il est mentionné qu’une fonctionnalité équivalente serait en préparation côté OCaml
-
Exemples de documentation
- Exemples de documentation pour les listes en Haskell
- Exemples de documentation pour les listes en OCaml
- Même pour des fonctions dont le résultat semble évident, une documentation riche en exemples donne immédiatement une idée concrète de la façon d’utiliser l’API
Conclusion : les deux conviennent à l’industrie, mais la préférence actuelle va à OCaml
- Les deux langages ont suffisamment évolué pour répondre à de vrais besoins industriels
- Ils restent toutefois de petits langages par rapport aux langages dominants
- Si l’on ne dépend pas de manière critique de l’existence d’un SDK particulier, on peut prendre plaisir à développer sa prochaine application avec l’un comme avec l’autre
- À l’heure actuelle, OCaml est jugé plus favorable à une concentration concrète sur la construction de choses
Aucun commentaire pour le moment.