Lazy IO a le problème que la libération de toute ressource que vous avez acquise est quelque peu imprévisible, car elle dépend de la façon dont votre programme consomme les données - son «modèle de demande». Une fois que votre programme supprime la dernière référence à la ressource, le GC finira par s'exécuter et libérer cette ressource.
Les flux paresseux sont un style très pratique pour programmer. C'est pourquoi les tubes shell sont si amusants et populaires.
Cependant, si les ressources sont limitées (comme dans les scénarios à hautes performances ou dans les environnements de production qui s'attendent à évoluer aux limites de la machine), le fait de se fier au GC pour nettoyer peut être une garantie insuffisante.
Parfois, vous devez libérer des ressources avec empressement, afin d'améliorer l'évolutivité.
Alors, quelles sont les alternatives aux IO paresseux qui ne signifient pas renoncer au traitement incrémentiel (qui à son tour consommerait trop de ressources)? Eh bien, nous avons foldl
un traitement basé, aka iteratees ou énumérateurs, introduit par Oleg Kannedov à la fin des années 2000 , et depuis popularisé par un certain nombre de projets basés sur le réseautage.
Au lieu de traiter les données sous forme de flux paresseux, ou dans un lot énorme, nous faisons plutôt abstraction d'un traitement strict basé sur des blocs, avec une finalisation garantie de la ressource une fois que le dernier bloc est lu. C'est l'essence de la programmation iteratee, et celle qui offre de très belles contraintes de ressources.
L'inconvénient des E / S basées sur iteratee est qu'il a un modèle de programmation quelque peu gênant (à peu près analogue à la programmation basée sur les événements, par opposition à un contrôle basé sur les threads). C'est définitivement une technique avancée, dans n'importe quel langage de programmation. Et pour la grande majorité des problèmes de programmation, lazy IO est entièrement satisfaisante. Cependant, si vous allez ouvrir de nombreux fichiers, ou parler sur plusieurs sockets, ou utiliser autrement de nombreuses ressources simultanées, une approche iteratee (ou énumérateur) peut avoir du sens.
Dons a fourni une très bonne réponse, mais il a laissé de côté ce qui est (pour moi) l'une des caractéristiques les plus convaincantes des iteratees: ils facilitent le raisonnement sur la gestion de l'espace car les anciennes données doivent être explicitement conservées. Considérer:
Il s'agit d'une fuite d'espace bien connue, car la liste entière
xs
doit être conservée en mémoire pour calculer à la foissum
etlength
. Il est possible de faire un consommateur efficace en créant un pli:Mais c'est quelque peu gênant de devoir faire cela pour chaque processeur de flux. Il y a quelques généralisations ( Conal Elliott - Beautiful Fold Zipping ), mais elles ne semblent pas avoir compris. Cependant, les iteratees peuvent vous procurer un niveau d'expression similaire.
Ce n'est pas aussi efficace qu'un repli car la liste est toujours itérée plusieurs fois, mais elle est collectée par blocs afin que les anciennes données puissent être efficacement récupérées. Pour casser cette propriété, il est nécessaire de conserver explicitement toute l'entrée, comme avec stream2list:
L'état des itérés en tant que modèle de programmation est un travail en cours, mais il est bien meilleur qu'il y a encore un an. Nous apprenons ce que combinateurs sont utiles (par exemple
zip
,breakE
,enumWith
) et qui le sont moins, avec le résultat qui a construit en iteratees et combinateurs fournissent sans cesse plus expressivité.Cela dit, Dons a raison de dire qu'il s'agit d'une technique avancée; Je ne les utiliserais certainement pas pour tous les problèmes d'E / S.
la source
J'utilise des E / S paresseuses dans le code de production tout le temps. Ce n'est un problème que dans certaines circonstances, comme Don l'a mentionné. Mais pour simplement lire quelques fichiers, cela fonctionne bien.
la source
Mise à jour: Récemment, sur haskell-cafe, Oleg Kiseljov a montré que
unsafeInterleaveST
(qui est utilisé pour implémenter des IO paresseux dans la monade ST) est très dangereux - cela brise le raisonnement équationnel. Il montre que cela permet de construire debad_ctx :: ((Bool,Bool) -> Bool) -> Bool
telle sorte quemême si elle
==
est commutative.Un autre problème avec les E / S différées: l'opération d'E / S réelle peut être différée jusqu'à ce qu'il soit trop tard, par exemple après la fermeture du fichier. Citation de Haskell Wiki - Problèmes avec IO paresseux :
C'est souvent une erreur inattendue et facile à faire.
Voir aussi: Trois exemples de problèmes avec E / S Lazy .
la source
hGetContents
etwithFile
est inutile car le premier met la poignée dans un état "pseudo-fermé" et gérera la fermeture pour vous (paresseusement) de sorte que le code est exactement équivalent àreadFile
, ou mêmeopenFile
sanshClose
. C'est essentiellement ce paresseux I / O est . Si vous ne l' utilisez pasreadFile
,getContents
ouhGetContents
vous ne l' utilisez E / S paresseux. Par exempleline <- withFile "test.txt" ReadMode hGetLine
fonctionne très bien.hGetContents
gérera la fermeture du fichier pour vous, il est également permis de le fermer vous-même «tôt», et permet de garantir que les ressources sont libérées de manière prévisible.Un autre problème avec lazy IO qui n'a pas été mentionné jusqu'à présent est son comportement surprenant. Dans un programme Haskell normal, il peut parfois être difficile de prédire quand chaque partie de votre programme est évaluée, mais heureusement en raison de sa pureté, cela n'a pas vraiment d'importance à moins que vous ayez des problèmes de performances. Lorsqu'une IO paresseuse est introduite, l'ordre d'évaluation de votre code a en fait un effet sur sa signification, de sorte que les changements que vous avez l'habitude de considérer comme inoffensifs peuvent vous causer de véritables problèmes.
À titre d'exemple, voici une question sur le code qui semble raisonnable mais qui est rendue plus confuse par les E / S différées: withFile vs openFile
Ces problèmes ne sont pas toujours fatals, mais c'est une autre chose à laquelle il faut penser, et un mal de tête suffisamment grave pour que j'évite personnellement IO paresseux à moins qu'il y ait un réel problème à faire tout le travail d'avance.
la source