Autant que je sache, une fonction polymorphe seq
est mauvaise car elle affaiblit les théorèmes libres, ou, en d'autres termes, certaines égalités qui sont valides sans seq
ne le sont plus avec seq
. Par exemple, l'égalité
map g (f xs) = f (map g xs)
est vraie pour toutes les fonctions g :: tau -> tau'
, toutes les listes xs :: [tau]
et toutes les fonctions polymorphes f :: [a] -> [a]
. En gros, cette égalité stipule que f
ne peut que réorganiser les éléments de sa liste d'arguments ou supprimer ou dupliquer des éléments mais ne peut pas inventer de nouveaux éléments.
Pour être honnête, il peut inventer des éléments car il pourrait "insérer" une computation non-terminale/une erreur d'exécution dans les listes, étant donné que le type d'une erreur est polymorphe. Autrement dit, cette égalité est déjà fausse dans un langage de programmation comme Haskell sans seq
. Les définitions de fonction suivantes fournissent un contre-exemple à l'équation. Essentiellement, du côté gauche, g
"cache" l'erreur.
g _ = True
f _ = [undefined]
Pour corriger l'équation, g
doit être strict, c'est-à-dire, il doit mapper une erreur sur une erreur. Dans ce cas, l'égalité est à nouveau vérifiée.
Si vous ajoutez un opérateur seq
polymorphe, l'équation est de nouveau fausse, par exemple, l'instanciation suivante est un contre-exemple.
g True = True
f (x:y:_) = [seq x y]
Si nous considérons la liste xs = [False, True]
, nous avons
map g (f [False, True]) = map g [True] = [True]
mais, d'un autre côté
f (map g [False, True]) = f [undefined, True] = [undefined]
Autrement dit, vous pouvez utiliser seq
pour faire en sorte que l'élément d'une certaine position de la liste dépende de la définition d'un autre élément dans la liste. L'égalité est à nouveau vérifiée si g
est total. Si vous êtes intéressé par les théorèmes libres, consultez le générateur de théorèmes libres, qui vous permet de spécifier si vous considérez un langage avec des erreurs ou même un langage avec seq
. Bien que cela puisse sembler avoir moins de pertinence pratique, seq
brise certaines transformations qui sont utilisées pour améliorer les performances des programmes fonctionnels, par exemple, la fusion foldr
/build
échoue en présence de seq
. Si vous êtes intéressé par plus de détails sur les théorèmes libres en présence de seq
, consultez Théorèmes libres en présence de seq.
Autant que je sache, il avait été connu qu'un seq
polymorphe brise certaines transformations lorsqu'il a été ajouté au langage. Cependant, les alternatives ont également des inconvénients. Si vous ajoutez un seq
basé sur des classes de types, vous pourriez devoir ajouter de nombreuses contraintes de classe de types à votre programme, si vous ajoutez un seq
quelque part en profondeur. De plus, il n'était pas possible de ne pas inclure seq
car on savait déjà qu'il y avait des fuites d'espace qui pouvaient être corrigées en utilisant seq
.
Enfin, je pourrais ne pas être au courant de quelque chose, mais je ne vois pas comment un opérateur seq
de type a -> a
fonctionnerait. L'intérêt de seq
est qu'il évalue une expression sous forme normale de tête, si une autre expression est évaluée sous forme normale de tête. Si seq
a le type a -> a
, il n'y a aucun moyen de faire dépendre l'évaluation d'une expression de l'évaluation d'une autre expression.
25 votes
La fonction
seq
n'est pas lambda définissable (c'est-à-dire qu'elle ne peut pas être définie dans le calcul lambda), ce qui signifie que tous les résultats du calcul lambda ne peuvent plus être fiables lorsque nous avonsseq
.0 votes
Patricia Johann et Janis Voigtlander fournissent des détails dans leur article L'impact de seq sur les transformations de programme basées sur des théorèmes gratuits.
0 votes
Donc,
seq
, tout commeIO
, a une implémentation mais pas de dénotation (du moins en tant que terme dans le calcul lambda). Pour sauver cette confiance perdue, faudrait-il bannir à la foisseq
etIO
de Haskell?