Quitter Haskell derrière soi
(journal.infinitenegativeutility.com)- Un programmeur qui utilisait Haskell comme langage principal entre environ 2009 et 2019 n’en fait désormais plus son choix par défaut ni pour ses projets personnels ni pour sa recherche d’emploi
- L’attrait de Haskell ne venait pas tant des monades ou des DSL en eux-mêmes que du raisonnement algébrique sur le code, du refactoring sûr rendu possible par un système de types puissant, et de l’expression des invariants
- Cet éloignement s’explique par l’accumulation d’une culture qui valorise les fonctionnalités de typage avancées, d’outils peu fluides, et de changements incompatibles persistants dans GHC et la bibliothèque standard
- Malgré cela, les fonctions d’ordre supérieur, la notation
do, les lenses, les bibliothèques déclaratives et les abstractions de types avancées restent des atouts que les autres langages remplacent difficilement - Haskell vaut largement la peine d’être appris pour devenir un meilleur programmeur, mais en faire le langage par défaut d’une organisation suppose d’accepter le coût d’outils imparfaits et d’une stabilité limitée du langage
La période où Haskell était mon langage par défaut, et ma position actuelle
- Pendant environ 10 ans, Haskell a été mon langage par défaut pour les nouveaux projets
- Après avoir découvert Haskell vers 2009, je me suis considéré comme programmeur Haskell jusqu’à ce que je rejoigne vers 2019 une entreprise où j’utilisais surtout Ruby et C++
- J’ai utilisé Haskell pour des outils en ligne de commande, des services web, des applications graphiques, de petits scripts et d’autres usages variés
- Aujourd’hui, je ne rejette pas totalement Haskell, mais ce n’est plus mon langage par défaut, même pour des projets personnels, et je ne cherche plus délibérément un « emploi Haskell » comme autrefois
- Ce jugement ne revient pas à dire que Haskell est mauvais, mais se rapproche plutôt d’une explication personnelle de pourquoi je m’en suis éloigné
Ce qui m’attirait dans Haskell
-
La possibilité de manipuler le code algébriquement
- Le plus grand attrait de Haskell était la possibilité de raisonner sur le code de manière symbolique et algébrique
- Par exemple, avec une fonction
double n = n + n, la transformation dedouble xenx + xest toujours correcte en Haskell - Dans beaucoup de langages impératifs,
double(i++)et(i++) + (i++)peuvent se comporter différemment- le premier incrémente
iune fois - le second incrémente
ideux fois
- le premier incrémente
- Grâce à cette propriété, on peut empiler de petites transformations mécaniques pour effectuer de gros refactorings de manière plus sûre
- Un gros refactoring dans une base de code Ruby peut être très angoissant, alors qu’il peut être bien plus simple dans une base de code Haskell
-
Un système de types strict, sans être excessivement bavard
- Le système de types de Haskell est strict, mais grâce à l’inférence de types, il n’exige pas une verbosité excessive comme en Java
- Les typeclasses réduisent aussi les frictions quand il faut faire attention aux types
- Un bon code Haskell possède une forme de fragilité utile
- une mauvaise abstraction ne se tord pas silencieusement pour laisser passer un programme incorrect
- s’il y a un problème, la compilation échoue
- Le type
NonEmptyreprésente une liste contenant au moins un élément- une opération qui préserve la longueur comme
mapretourne unNonEmpty - une opération qui allonge la liste comme
appendretourne aussi unNonEmpty - une opération comme
filter, dont le résultat peut être vide, retourne une liste ordinaire
- une opération qui préserve la longueur comme
- Si une fonction exige qu’une liste de chemins de fichiers soit un
NonEmpty, on ne peut pas simplement lui passer les arguments de ligne de commande : il faut d’abord vérifier qu’ils ne sont pas vides - Si l’on ajoute ensuite une étape de filtrage par extension, le résultat peut redevenir vide ; il faut donc vérifier à nouveau, et le compilateur empêche qu’on passe accidentellement une liste vide
-
Maintenir les invariants via les types
- À force d’utiliser Haskell, on s’habitue à exprimer les invariants d’un programme dans les types et à laisser le compilateur les vérifier
- Cela ne signifie pas uniquement faire des calculs complexes au niveau des types
- On peut, par exemple, distinguer l’état d’échappement SQL avec des types enveloppes comme
RawStringetSanitizedString- même si une API web fournit un
RawString, on peut faire en sorte que la couche base de données n’accepte que desSanitizedString
- même si une API web fournit un
- On peut transformer une représentation de surface pleine de champs
Maybeen une représentation interne qui garantit la présence des informations nécessaires - On peut aussi viser un niveau de généricité suffisant pour permettre des tests indépendants, sans aller plus loin
-
Un langage où les DSL et les fonctions d’ordre supérieur sont naturels
- Haskell facilite la création de langages spécifiques au domaine concis, même sans macros
- config-ini est un exemple de bibliothèque pour manipuler des fichiers de configuration INI
- elle utilise une interface déclarative plutôt qu’une interface de parsing impérative
- elle mappe certaines parties d’un type de configuration à certaines parties de la structure d’un fichier INI
- la même interface permet la lecture, l’écriture et les mises à jour minimales par diff
- Là où un DSL Ruby peut dépendre de métaprogrammation dynamique et de monkeypatching global, un DSL Haskell se limite plus facilement à un fichier ou à une zone de code précise grâce à une syntaxe souple et à des fonctionnalités comme la surcharge des monades
- Haskell rend aussi le travail avec les fonctions d’ordre supérieur très naturel
(+)peut être utilisé directement comme fonction qui prend deux nombres et les additionnepairwiseProduct = zipWith (*)permet d’exprimer très brièvement le produit élément par élément de deux listes
- L’idée que Haskell permet de manipuler les fonctions comme Perl permet de manipuler les chaînes de caractères est une comparaison assez juste
Trois raisons de mon éloignement de Haskell
-
Une ambiance qui privilégie les styles difficiles
- Le premier facteur est une préférence stylistique pour la nouveauté technique
- La communauté Haskell expérimente et construit sans cesse de nouvelles abstractions
- des approches issues de l’algèbre abstraite ou de la théorie des catégories
- des approches qui remontent davantage de calcul au niveau des types pour restreindre les états invalides
- Les API web au niveau des types de Servant ou le cas d’un système de menus exprimé avec un comonad sont des expériences cherchant à représenter un domaine de problème d’une manière nouvelle
- Mais dans le code du quotidien, ces approches expérimentales peuvent créer plus de problèmes qu’elles n’apportent d’avantages au premier regard
- Dans un exemple de projet de compilateur, les nœuds de l’AST portaient le type d’expression comme paramètre de type
- si l’on construisait un arbre syntaxique avec une erreur de type, le compilateur lui-même ne compilait plus
- au départ, cela a permis de détecter quelques bugs
- mais lorsque de nombreuses passes d’optimisation ne respectaient pas bien cette discipline de typage, le code s’est compliqué avec des casts et des contorsions pour apaiser GHC
- au final, le paramètre de type a été retiré de l’AST
- Sur les projets qui ont tenté l’approche des « fancy types », environ trois quarts ont été jugés comme n’en valant pas vraiment la peine
- Le courant Simple Haskell repose sur l’idée qu’on tire le meilleur du langage en évitant les fonctionnalités expérimentales et en se limitant à un minimum d’extensions
- Les fonctionnalités de typage plus complexes sont pourtant largement répandues dans la communauté et les bibliothèques
- Les abstractions de types complexes peuvent rendre les changements et la maintenance plus difficiles, et j’ai déjà vu des tentatives de remplacer des abstractions simplifiées par les dernières abstractions au niveau des types aboutir à davantage de code, de moins bonnes performances et une lisibilité dégradée
- Cela dit, Trees that Grow reste pour moi un exemple utile de fonctionnalité « type-level » sophistiquée, avec des structures de données indexées selon les phases du compilateur
-
Des outils qui ne brillent pas au quotidien
- Le deuxième facteur est un écosystème d’outils maladroit
- Haskell dispose de plusieurs systèmes de build et outils de développement proches de standards de fait
- Ces outils faisaient globalement ce qu’on leur demandait, mais sans atteindre un niveau vraiment « attachant »
- Certains outils comme
ormolufaisaient le travail avec un minimum de friction, mais d’autres exigeaient des contournements constants à cause de bugs persistants - Les outils Haskell se sont améliorés en dix ans, mais en 2019, rien n’approchait vraiment l’ergonomie et la stabilité de Rust Cargo
- Une tendance Not-Invented-Here, consistant à toujours vouloir recréer quelque chose de nouveau et de meilleur, posait aussi problème
- il existe même un courant voulant retraiter le format texte des paquets via hpack
- il est frustrant de voir ce type d’initiative rester bloqué pendant des années à 95 % de complétude
- Il ne s’agit pas de dire que les outils Haskell sont mauvais, mais « plutôt corrects » ne suffit plus à satisfaire des standards devenus plus élevés
-
Des changements incompatibles permanents
- Le troisième facteur est le changement permanent du langage Haskell et de GHC
- GHC est en pratique considéré comme la seule implémentation publique de Haskell réellement raisonnable
- Haskell cherche progressivement à corriger ses erreurs et incohérences passées, y compris dans la bibliothèque standard et les éléments fondamentaux du langage
- Ces changements peuvent conduire à un socle plus propre et plus cohérent, mais ils créent aussi en continu de petites frictions pour les utilisateurs
- Les Foldable-Traversable Prelude changes de 2015 en sont un exemple
- Un ancien projet Haskell peut ne pas compiler avec la dernière version de GHC, même sans dépendances externes
- ajout d’une superclass
- suppression d’un import
- ajout d’une signature de type à cause d’un polymorphisme devenu ambigu
- Les corrections sont généralement petites, mais elles s’accumulent, et ces écarts subtils avec le passé demandent une attention constante
- À l’inverse, la méthode des mises à jour par epochs de Rust explicite un contrat de compatibilité descendante pour les fonctionnalités stables et masque les breaking changes intentionnels derrière un changement d’epoch
Les qualités de Haskell qui me manquent encore
- L’écriture algébrique du code et le refactoring en Haskell restent de vraies forces
- Rust partage beaucoup d’atouts avec Haskell, mais il est moins facile d’avoir la même confiance que de petits refactorings mécaniques préserveront le sens du programme
- Le système de types fournit aussi des capacités que les autres langages n’offrent généralement pas
- En 2023, les langages grand public ont beaucoup progressé en typage statique par rapport à 2009
- Malgré cela, des capacités comme les abstractions au-dessus des higher-kinded types restent des éléments distinctifs de Haskell
- L’écosystème de bibliothèques Haskell est contrasté
- des bibliothèques open source à moitié maintenues
- de petits royaumes où chaque auteur reconstruit le monde à sa manière
- des types proches de chaînes de caractères, nécessaires mais incompatibles entre eux
- Malgré cela, Haskell possède d’excellentes bibliothèques et abstractions qu’on ne retrouve que partiellement ailleurs
- les lenses sont une abstraction puissante dont les avantages apparaissent vraiment après un usage prolongé
- Brick propose une excellente bibliothèque d’interface terminale à base de blocs déclaratifs
- diagrams et music-suite sont aussi de bons exemples d’API déclaratives
- La notation
doest aussi une fonctionnalité qui me manque beaucoup- l’essentiel n’est pas tant
dolui-même qu’une notation où l’on peut ajouter des liaisons sans augmenter sans cesse le niveau d’indentation - des
flat_mapimbriqués en Ruby deviennent de plus en plus profonds à mesure qu’on ajoute des axes - la notation
dode Haskell exprime la même composition de manière plate
- l’essentiel n’est pas tant
- La notation
dopermet aussi d’embarquer, avec la même légèreté syntaxique, de la concurrence structurée façonasync/await, des transactions concurrentes et des programmes probabilistes
Faut-il apprendre Haskell, faut-il l’utiliser ?
- La réponse à « faut-il apprendre ce langage, faut-il l’utiliser ? » dépend de l’objectif
- Si le but est de devenir un meilleur programmeur, Haskell mérite largement d’être appris
- Haskell est un langage intéressant et puissant
- L’expérience d’avoir appris Haskell est jugée comme ayant amélioré les compétences de programmation
- Si le but est l’usage concret, il faut être plus prudent
- les avantages de Haskell sont bien réels
- mais il faut les mettre en balance avec le niveau de friction qu’une personne ou une organisation peut accepter
- certaines personnes et certaines organisations tirent suffisamment de bénéfices de Haskell pour que ces petites douleurs restent supportables
- Si l’on exige une réponse nette par oui ou par non, on se rapproche de « n’utilisez pas Haskell »
- si je devais construire une organisation d’ingénierie, je ne choisirais pas Haskell comme langage par défaut
- parce que je connais à la fois la valeur de bons outils et le coût de mauvais outils
- je préfère un langage capable d’offrir immédiatement un environnement stable, un écosystème de code clair et d’excellents outils
- Malgré tout, Haskell fournit des outils remarquables pour la sûreté et la justesse des programmes
- dans certains contextes, cela peut valoir la peine de supporter de mauvais outils et du linting destiné à bloquer les
type familyinutiles
- dans certains contextes, cela peut valoir la peine de supporter de mauvais outils et du linting destiné à bloquer les
- Haskell n’est plus un langage qui me convient personnellement, mais si Haskell n’avait pas existé dans le monde, le monde aurait été pire
1 commentaires
Avis sur Hacker News
Après avoir utilisé Haskell pendant une dizaine d’années, je m’en suis récemment éloigné comme moyen de subsistance, et je suis clairement d’accord avec le premier point de l’auteur : la communauté Haskell accorde une très grande importance à l’apprentissage.
C’est agréable de travailler avec des personnes curieuses, avec toujours quelque chose à enseigner et à apprendre, mais la communauté est moins douée pour abandonner des idées expérimentées ; dans les bases de code Haskell professionnelles, si elles ne sont pas strictement encadrées, beaucoup de « choses qu’on peut faire, mais qu’il vaut mieux ne pas utiliser » ont tendance à s’accumuler.
En revanche, je ne suis pas d’accord concernant la toolchain. Oui, les outils Haskell ne sont pas terribles, mais après avoir utilisé Python, JS, Java, Rust et Elm, j’ai trouvé que la plupart des outils étaient eux aussi médiocres, et la toolchain Haskell m’a même manqué. cargo est excellent, mais c’est plutôt l’exception ; je me demande si Rust pourra éviter cet état de fait une fois qu’il aura vieilli.
J’ai un projet sur lequel je travaille de temps en temps, et je suis sûr à 100 % que la prochaine fois que je l’ouvrirai, il ne fonctionnera plus. La gestion des dépendances et des environnements virtuels de Python est tout aussi mauvaise, mais Poetry était plutôt correct ; avec Ruby, que l’on utilise chruby, rbenv ou rvm, tout fonctionne à peu près aussi bien, et Bundler tourne correctement. Java a ses défauts, mais l’expérience IDE est de tout premier ordre, et même si le refactoring n’est pas aussi sûr qu’en Haskell, il est très facile à faire.
Bundler n’est globalement pas mauvais, et même s’il y a des choix de conception avec lesquels je ne suis pas d’accord, beaucoup de choses s’emboîtent bien si l’on suit la bonne voie. Les outils de langage peuvent eux aussi être conçus d’une manière suffisamment moins cassée, et même si je n’ai pas beaucoup utilisé cargo directement, s’il est vraiment le meilleur de sa catégorie, il ne serait pas surprenant qu’il le reste.
Côté outils, Haskell a, à ma connaissance, quelque chose avec lequel les autres langages ont du mal à rivaliser : Hoogle.
Hoogle est étonnamment excellent. On lui dit en Haskell ce que l’on veut, et il indique ce que l’on peut faire en Haskell. Il y a eu des tentatives similaires pour Rust, et j’ai aussi essayé de créer Noogle pour Nim, mais dans les langages où « passer un argument à une fonction » et « appeler une fonction » sont clairement distincts, cela ne fonctionne pas de la même manière.
Chaque fois que je vois du code Rust mêlant
ResultetBox, j’aimerais avoir un Roogle aussi utile que Hoogle. Pour le reste des outils Haskell, je ne leur trouve pas d’avantage. Cela semble en grande partie dû à l’instinct de la communauté qui consiste à vouloir créer quelque chose d’« innovant » plutôt qu’à améliorer l’existant.Ce n’est pas parfait, mais c’est bien meilleur qu’il y a cinq ans.
J’aimerais que davantage de projets prennent cette direction.
À mes yeux, le plus gros problème est que GHC est lié à une version précise de
base, la bibliothèque standard.Quand
basechange et qu’une nouvelle version de GHC ne prend en charge que cette version-là, il faut en pratique modifier toutes les dépendances pour utiliser un GHC plus récent. Je ne comprends toujours pas pourquoi du code compilé avec GHC 9.6 devrait utiliserbase4.18.0.0. Pourquoi le binaire GHC devrait-il se soucier de la version du moduleData.Listdu code qu’il compile ?Je comprends que les éléments spécifiques à GHC exposés par
basesoient liés à une version particulière de GHC, mais je ne vois pas pourquoi tout le reste devrait l’être. Cela dit, je crois comprendre qu’un travail est en cours pour corriger cela en divisantbaseen plusieurs paquets : https://gitlab.haskell.org/ghc/ghc-wiki-mirror/-/blob/master...baseen le considérant comme invalide.bases’échangent des instances deData.List, cela pose problème.Par exemple, si l’on a une liste de
Data.List, peut-on y concaténer les résultats de fonctions provenant de deux bibliothèques différentes ?baseest un objectif que tout le monde souhaite atteindre. La situation actuelle tient largement à des raisons historiques et au fait que des éléments fondamentaux liés aux internes du compilateur ont été couplés à des éléments non fondamentaux ; avec suffisamment d’efforts, cela peut s’améliorer.S’il y avait trois grandes raisons pour lesquelles mon intérêt pour Haskell s’est progressivement refroidi, ce seraient la préférence stylistique pour la nouveauté, qui glorifie le code obscur et rend la maintenance difficile, des outils maladroits qui rendent les tâches quotidiennes plus pénibles, et des changements continus qui, même s’ils ne sont qu’occasionnels, demandent une attention constante et cassent régulièrement des choses.
C’est une critique valable. J’ai beaucoup appris Haskell entre 2010 et 2012 ; le langage lui-même était excellent, mais la documentation et les outils étaient difficiles à apprivoiser. La communauté est passée de Cabal — et du tristement célèbre Cabal hell — à Stack, puis est revenue à Cabal, et dans l’ensemble les choses se sont améliorées.
Par ailleurs, d’autres langages de programmation ont eux aussi absorbé des éléments de programmation fonctionnelle. Par exemple, Java a ajouté les Streams, les fonctions, les lambdas, les types de données algébriques, les records et le pattern matching ; même si la syntaxe n’est pas aussi élégante que celle de Haskell, les notions de base de la programmation fonctionnelle y sont présentes.
Ils se sont ainsi créé eux-mêmes une fosse de souffrance sans fin, rendue à peine supportable parce que les rayons de lumière ne descendent pas assez bas pour leur permettre de voir les couleurs que la lumière dessine sur l’arbre dehors. Ceux qui viennent de l’extérieur se retrouvent déchirés entre un dialecte adapté à cette obscurité qu’ils ont eux-mêmes créée et un dialecte qui prospère dans la lumière mais suffoque là-bas, et ils perdent rapidement la raison.
Si la communauté est revenue à Cabal, je me demande ce que j’ai manqué.
Pour ces raisons, je suis passé à F# et je n’ai guère regardé en arrière. Les types de genre supérieur me manquent parfois, mais F# a tout de même les génériques et, pour être honnête, il m’oblige d’une certaine manière à écrire du code plus simple qu’en Haskell, ce qui donne généralement de meilleurs résultats.
Cela dit, F# ne donne pas autant l’impression d’être un « langage de gourou » que Haskell. C’est comme Haskell, mais résolument pragmatique. Je fais beaucoup de choses en F# et j’y prends plaisir, mais il m’arrive de repenser avec un peu de nostalgie à ce qui n’a pas marché avec Haskell.
Je me suis souvent demandé si je ne passais pas à côté de quelque chose en n’utilisant pas Haskell, mais pour mes objectifs ce n’est probablement pas le cas.
Apprendre Haskell, c’est un peu comme essayer de déchiffrer une langue extraterrestre. Si vous êtes habitué aux boucles if-else ordinaires et aux simples affectations de variables, préparez-vous à vous retourner le cerveau.
Une monad n’est pas un monstre cosmique, mais un concept abstrait étrange qui peut vous faire vous gratter la tête et remettre en question vos choix de vie. Côté bibliothèques, trouver ce dont on a besoin donne l’impression de fouiller dans une boutique d’occasion à moitié vide, et la documentation se lit à moitié comme des hiéroglyphes.
Même les performances, dans la réalité, peuvent vous amener à vous demander longtemps pourquoi l’évaluation paresseuse consomme plus de mémoire que prévu et ralentit le programme. Les emplois sont aussi difficiles à trouver, sauf dans des entreprises de niche qui misent tout sur Haskell, et le débogage ressemble à chercher une aiguille dans une botte de hiéroglyphes fonctionnels.
J’ai utilisé Scala, et je pense qu’il peut offrir le meilleur des deux mondes. On peut raisonner algébriquement, et quand ça compile après un refactoring, ça fonctionne généralement.
L’inférence de types et les monads fonctionnent bien aussi, tout en bénéficiant de l’écosystème Java. Quand ça devient trop obscur, on peut aussi enfreindre les règles et coder d’une manière plus proche de Java. Je serais curieux de savoir ce que les autres en pensent.
En revanche, sbt me rend fou, et c’est probablement ce que j’aime le moins dans l’expérience de développement.
Les instances de gouvernance et les mainteneurs de Haskell semblent réellement vouloir que le langage soit favorable à la recherche, l’expérimentation et l’exploration académique.
En soi, ce n’est pas un problème, mais à un moment donné cela entre forcément en conflit avec les intérêts des programmeurs qui veulent utiliser Haskell comme un outil pratique et stable. Ce qu’il faudrait, à mon avis, c’est la possibilité de marquer les éléments expérimentaux et à la pointe comme appartenant à une piste séparée. Que ce soit via des pragmas ou des déclarations au niveau des packages, il faudrait pouvoir savoir, rien qu’en regardant un package, si on veut l’intégrer ou non.
Les développeurs orientés production pourraient alors adopter des politiques du type « nous n’utilisons que du Haskell stable ». Tout en reconnaissant la dynamique d’« Avoid success at all costs », selon laquelle une adoption trop forte de Haskell risquerait d’enfermer le langage dans l’inertie et de l’empêcher de pousser des concepts de pointe, il faudrait une piste officielle permettant aux développeurs du quotidien de rester dans un sous-ensemble plus restreint et plus stable de Haskell.
Java n’aura jamais rien de ce genre. https://www.stackage.org/
Au final, la simplicité dépend du point de vue. Même les fonctionnalités de types sophistiquées peuvent, une fois leur fonctionnement intériorisé, rendre les types plus expressifs, le code plus sûr, et même plus simple.
C’est un très bon billet. Le seul point faible, c’est que je n’ai jamais ressenti de pression me poussant à utiliser telle ou telle nouvelle fonctionnalité de types sophistiquée si je n’en avais pas envie.
Si l’API HTTP au niveau des types de Servant ne vous plaît pas, vous pouvez descendre d’un cran et utiliser warp. Les bibliothèques comprennent que des types excessivement complexes peuvent devenir un piège, et elles sont donc souvent organisées en couches de cette manière. Il faut avoir une intuition de jusqu’où aller, et cela implique aussi de faire des erreurs en chemin.
J’ai lu la documentation de Servant, mais, sans surprise, elle supposait une compréhension de Haskell supérieure à la mienne.
Ce billet me parle vraiment. J’utilise Haskell depuis environ un an et demi sur une application assez industrielle, et j’avais déjà pas mal d’expérience auparavant avec des choses comme https://github.com/mattgreen/hython.
C’est la première fois depuis longtemps, en vingt ans de carrière, que j’ai dû apprendre autant de choses, et c’est le résultat de la combinaison du domaine et du langage. C’est à la fois enthousiasmant et épuisant, et cela peut représenter un gros morceau à encaisser quand on a une vie de famille bien remplie.
Malgré tout, le langage est excellent. Mon manager conseille généralement de l’utiliser de manière top-down : l’idée est que, comme la flexibilité de Haskell permet de bien communiquer l’intention de développement, il faut réfléchir en profondeur à la manière dont le code doit être lu, puis partir de là. C’est un vrai changement de mentalité par rapport à la plupart des langages, où l’on tape
functionà répétition pendant que le cerveau passe en mode économie d’énergie ; ici, on a vraiment l’impression que le langage est conçu pour être agréable à écrire.Mais même en gérant les effets contrôlés avec des monades, la syntaxe reste difficile à parcourir rapidement. Je ne sais pas si c’est parce qu’une ligne peut devenir trop dense. Les retours anticipés me manquent énormément, tout comme l’excellent LSP de Rust. Haskell a récemment perdu la complétion des pattern matches à cause des changements internes de la branche 9.x, empiler des monades me déplaît toujours et je me trompe souvent. Les temps de compilation peuvent être rudes, surtout dès que la bibliothèque lens entre en jeu.
Je n’aime pas non plus l’évaluation paresseuse par défaut. La communauté reconnaît aussi, dans une certaine mesure, que ce défaut rend les programmes Haskell beaucoup plus vulnérables aux fuites de mémoire, qui peuvent rester assez longtemps inaperçues dans beaucoup de programmes. En tant que programmeur système, cela donne envie de hurler.
Cela dit, la communauté fait partie des groupes de programmeurs les plus solides que j’aie vus. Je ne veux pas dérouler le cliché des gens « intelligents » façon mème ; le point important, c’est qu’ils réfléchissent vraiment en profondeur et poussent les idées vers l’avant. Dans notre domaine, l’analyse de programmes, Haskell va comme un gant, mais je reste un peu plus prudent pour le reste. Au fond, j’ai toujours soif d’un langage de programmation qui serait OCaml, mais avec une meilleure syntaxe.
Il n’est même pas nécessaire d’exposer ExceptT dans l’interface ; on peut l’utiliser uniquement en interne pour les retours anticipés, puis faire en sorte que la fonction retourne un Either.
C’est dommage qu’il n’ait finalement pas vraiment gagné en traction.
Il intègre mieux la programmation orientée objet et la programmation fonctionnelle qu’OCaml, et il est suffisamment proche pour que du code OCaml idiomatique soit assez facile à porter par une simple transformation.