Par exemple, interroger la base de données ou écrire un fichier ne peut pas être fait dans un pur style fonctionnel par définition. C'est par exemple, l'une des raisons pour lesquelles nous avons besoin de monades.
Personne n'a "besoin" de monades, ce n'est qu'une façon de décrire les choses. En fait, ce n'est probablement même pas le meilleur moyen. Une certaine forme de typage d'effet , des types d'unicité ou un système basé sur une logique linéaire complète semblent plus convaincants en théorie, mais sont tous des écarts plus radicaux par rapport à des systèmes de type bien connus et plus compliqués à exprimer. Les entrées-sorties monadiques trouvées dans Haskell sont un compromis entre la convivialité et la simplicité, car elles modélisent essentiellement la programmation entièrement impérative d'une manière qui coexiste facilement avec le système de type ML existant déjà utilisé dans le langage.
La question est - pourquoi nous considérons la sortie STDOUT comme quelque chose d'impur? Oui, tout gestionnaire de fichiers est risqué - nous ne pouvons jamais être sûrs que les données seront toujours écrites. Mais qu'en est-il de STDOUT? Pourquoi devrions-nous considérer cela comme quelque chose de peu fiable? Est-il plus peu fiable que l'évaluation elle-même? Je veux dire, nous pouvons toujours appuyer sur la gâchette et ainsi, interrompre le calcul.
Ce n'est pas le cas, et nous ne le faisons pas. L'entrée et la sortie du programme dans son ensemble peuvent être simplement considérées comme des arguments et résultent du traitement de l'ensemble du programme comme une grande fonction pure. Tant qu'il imprime la même chose sur stdout si vous l'alimentez depuis stdin, c'est toujours une fonction pure. En fait, avant d'introduire des E / S monadiques, Haskell utilisait un système d'E / S basé sur des flux qui utilisait des flux paresseux purs pour l'entrée et la sortie. Il l'a laissé tomber car c'était apparemment une douleur à utiliser, ce qui peut vous donner une idée de pourquoi vous n'avez jamais entendu parler de quelque chose comme ça. :]
Pour faire le point d'une manière plus stupide, considérons le langage ésotérique minimaliste, Lazy K :
Lazy K est un langage de programmation fonctionnel récupéré et transparent par référence, avec un système d'E / S simple basé sur des flux.
Ce qui distingue Lazy K des autres langages, c'est son absence presque totale d'autres fonctionnalités. Il n'offre pas, par exemple, un système de type polymorphe Hindley-Milner intégré. Il n'est pas livré avec une bibliothèque standard étendue avec prise en charge de la programmation d'interface graphique indépendante de la plate-forme et des liaisons vers d'autres langages. Une telle bibliothèque ne pourrait pas non plus être écrite car, entre autres, Lazy K ne fournit aucun moyen de définir ou de faire référence à des fonctions autres que les fonctions intégrées. Cette incapacité est complétée par un manque de prise en charge correspondant pour les nombres, les chaînes ou tout autre type de données. Néanmoins, Lazy K est Turing-complet.
(...)
Les programmes Lazy K vivent dans le même domaine platonicien intemporel que les fonctions mathématiques, ce que la page Unlambda appelle «le domaine béni du pur calcul lambda non typé». Tout comme la récupération de place cache le processus de gestion de la mémoire au programmeur, la transparence référentielle masque le processus d'évaluation. Le fait qu'un certain calcul soit nécessaire pour visualiser une image de l'ensemble Mandelbrot, ou pour "exécuter" un programme Lazy K, est un détail d'implémentation. C'est l'essence même de la programmation fonctionnelle.
(...)
Comment gérer les entrées et sorties dans une langue sans effets secondaires? Dans un certain sens, l'entrée et la sortie ne sont pas des effets secondaires; ce sont, pour ainsi dire, des effets avant et arrière. C'est donc dans Lazy K, où un programme est simplement traité comme une fonction de l'espace des entrées possibles à l'espace des sorties possibles.
Je doute que vous trouverez un langage plus purement fonctionnel que ça!
Gardez à l'esprit, cependant, que ce qui précède ne s'applique qu'à prendre essentiellement l'entrée et la sortie d'une fonction pure et à les connecter à stdin / stdout "en externe" d'une manière ou d'une autre. Il y a une grande différence entre cela et l'accès aux vraies primitives d'E / S au niveau du système. Les détails d'implémentation de la lecture et de l'écriture dans les flux peuvent fuir l'impureté à moins d'être soigneusement encapsulés.
Je pense que c'est la principale raison pour laquelle vous ne pouvez pas le faire directement dans Haskell - les cas d'utilisation raisonnables sont minces par rapport à l'utilisation d'E / S monadiques, et pour ce dernier, il y a beaucoup d'avantages à avoir accès à la vraie chose. Je crois que c'est pourquoi, par exemple, les arguments de ligne de commande du programme ne sont pas simplement transmis comme arguments main
, même s'il semble intuitivement qu'ils devraient l'être.
Vous pouvez cependant récupérer une version minimale de quelque chose comme ça dans un programme spécifique - capturez simplement les arguments en tant que valeurs pures, puis utilisez la interact
fonction pour le reste de votre programme.