Je trouve l'évaluation paresseuse utile pour un certain nombre de choses.
Premièrement, toutes les langues paresseuses existantes sont pures, car il est très difficile de raisonner sur les effets secondaires dans une langue paresseuse.
Les langages purs vous permettent de raisonner sur les définitions de fonctions en utilisant le raisonnement équationnel.
foo x = x + 3
Malheureusement, dans un paramètre non paresseux, plus d'instructions échouent que dans un paramètre paresseux, ce qui est donc moins utile dans des langages comme ML. Mais dans un langage paresseux, vous pouvez raisonner en toute sécurité sur l'égalité.
Deuxièmement, beaucoup de choses comme la «restriction de valeur» dans ML ne sont pas nécessaires dans les langages paresseux comme Haskell. Cela conduit à un grand désencombrement de la syntaxe. ML comme les langages doivent utiliser des mots-clés comme var ou fun. Dans Haskell, ces choses se résument à une seule notion.
Troisièmement, la paresse vous permet d'écrire un code très fonctionnel qui peut être compris en morceaux. Dans Haskell, il est courant d'écrire un corps de fonction comme:
foo x y = if condition1
then some (complicated set of combinators) (involving bigscaryexpression)
else if condition2
then bigscaryexpression
else Nothing
where some x y = ...
bigscaryexpression = ...
condition1 = ...
condition2 = ...
Cela vous permet de travailler «de haut en bas» grâce à la compréhension du corps d'une fonction. Les langages de type ML vous obligent à utiliser un let
qui est évalué strictement. Par conséquent, vous n'osez pas «soulever» la clause let vers le corps principal de la fonction, car si elle est coûteuse (ou a des effets secondaires), vous ne voulez pas qu'elle soit toujours évaluée. Haskell peut «pousser» les détails vers la clause where explicitement car il sait que le contenu de cette clause ne sera évalué que si nécessaire.
En pratique, nous avons tendance à utiliser des gardes et à les réduire pour:
foo x y
| condition1 = some (complicated set of combinators) (involving bigscaryexpression)
| condition2 = bigscaryexpression
| otherwise = Nothing
where some x y = ...
bigscaryexpression = ...
condition1 = ...
condition2 = ...
Quatrièmement, la paresse offre parfois une expression beaucoup plus élégante de certains algorithmes. Un `` tri rapide '' paresseux dans Haskell est une ligne unique et présente l'avantage que si vous ne regardez que les premiers articles, vous ne payez que des coûts proportionnels au coût de sélection de ces articles. Rien ne vous empêche de le faire strictement, mais vous devrez probablement recoder l'algorithme à chaque fois pour obtenir les mêmes performances asymptotiques.
Cinquièmement, la paresse vous permet de définir de nouvelles structures de contrôle dans la langue. Vous ne pouvez pas écrire une nouvelle construction comme «si… alors… autre…» dans un langage strict. Si vous essayez de définir une fonction comme:
if' True x y = x
if' False x y = y
dans un langage strict, les deux branches seraient évaluées indépendamment de la valeur de la condition. Cela empire quand on considère les boucles. Toutes les solutions strictes nécessitent que le langage vous fournisse une sorte de citation ou de construction lambda explicite.
Enfin, dans la même veine, certains des meilleurs mécanismes pour faire face aux effets secondaires dans le système de types, tels que les monades, ne peuvent vraiment être exprimés efficacement que dans un cadre paresseux. Ceci peut être observé en comparant la complexité des workflows de F # aux monades Haskell. (Vous pouvez définir une monade dans un langage strict, mais malheureusement, vous échouerez souvent à une ou deux lois de la monade en raison d'un manque de paresse et les flux de travail en comparaison ramassent une tonne de bagages stricts.)