STDOUT et son impureté


10

J'ai lu beaucoup de livres et d'articles sur la programmation fonctionnelle et j'ai toujours honte de ne pas pouvoir comprendre à coup sûr certains concepts très basiques.

L'une des principales idées de la programmation fonctionnelle est que la même entrée doit toujours produire la même sortie. 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.

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.

Réponses:


6

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 interactfonction pour le reste de votre programme.


Monsieur, je dois avouer, j'apprécie votre réponse sur l'une des piles. Vous devriez certainement écrire un livre Haskell et je ne plaisante pas.
shabunc

@shabunc: Je me suis parfois demandé à quel point la somme totale de mes réponses sur SO était déjà de la taille d'un livre ...
CA McCann

Pourriez-vous donner un exemple de système basé sur une logique linéaire complète? Cela semble intéressant, s'il existe.
configurateur

@configurator: Remarquez comment j'ai lié des langues spécifiques pour les autres, mais une page wikipedia pour la logique linéaire? Hélas, si j'avais eu un exemple, je l'aurais donné. : [Tout ce dont j'ai entendu parler, ce sont des prototypes partiels et des systèmes expérimentaux issus de la recherche CS. Si vous souhaitez approfondir cela, voici du matériel relativement accessible sur les systèmes de type linéaire qui pourrait vous aider à démarrer.
CA McCann, le

3

Bien que la pureté dans un programme fonctionnel soit un objectif louable, la réalité est que chaque programme non trivial et utile aura une certaine impureté (ou «effets secondaires»), pour les raisons que vous avez mentionnées.

Les programmes complètement purs sont, par définition, une boîte noire scellée et sont essentiellement sans intérêt.

Le langage fonctionnel Haskell s'occupe de ce problème en isolant les effets secondaires tels que la sortie dans les monades . La monade conserve un style de programmation purement fonctionnel, tout en permettant de produire des sorties.


vous avez raison. Mais je comprends pourquoi la pureté à 100% est une utopie. Ma question concerne STDOUT.
shabunc

1
STDOUT est un effet secondaire, comme tout autre. En interne, la monade effectuerait toute vérification d'erreur qui pourrait être requise.
Robert Harvey,

oui, c'est de cela qu'il s'agit - pourquoi est-ce considéré comme un effet secondaire comme les autres?
shabunc

2
Tout ce qui modifie le monde extérieur est considéré comme un effet secondaire.
Robert Harvey,

1

L'écriture dans STDOUT peut échouer si vous n'êtes pas connecté à un terminal ou si (pour une raison quelconque) vous avez fermé le descripteur de fichier.

De plus, STDOUT n'est pas toujours "l'écran de la console". Parfois, il est acheminé vers un autre programme. Parfois, le tuyau est cassé.


0

Cela aide si vous pensez à la pureté en termes de "Changement de l'état du monde extérieur". Cela peut inclure l'écriture sur une console, un fichier journal, l'éjection du CD ou le «lancement des missiles».

Cela peut également être un problème en termes d'exécution simultanée. Si vous savez qu'une fonction n'a pas d'effets secondaires, vous pouvez facilement organiser la concurrence, car vous pouvez prouver qu'il ne peut y avoir de conditions de concurrence ou similaires.


Modifie l'état du monde extérieur ou dépend de l'état du monde extérieur. Voir cette question pour plus de discussion dans ce sens.
MatrixFrog
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.