Haskell a une fonction magique nommée seq
, qui prend un argument de n'importe quel type et le réduit à Forme normale de tête faible (WHNF).
J'ai lu quelques sources [pas que je me souvienne de qui elles étaient maintenant ...] qui prétendent que "polymorphe seq
est mauvais". En quoi sont-ils "mauvais"?
De même, il existe la fonction rnf
, qui réduit un argument à forme normale (NF). Mais this est une méthode de classe; cela ne fonctionne pas pour les types arbitraires. Il me semble "évident" que l'on pourrait modifier la spécification du langage pour fournir ceci comme une primitive intégrée, similaire à seq
. Ce serait, sans doute, "encore plus mauvais" que d'avoir simplement seq
. De quelle manière en est-il ainsi?
Enfin, quelqu'un a suggéré que donner seq
, rnf
, par
et similaires du même type que la fonction id
, plutôt que const
fonctionner tel qu’il est actuellement, serait une amélioration. Comment?
Pour 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 valable pour toutes les fonctions g :: tau -> tau'
, toutes les listes xs :: [tau]
et toutes les fonctions polymorphes f :: [a] -> [a]
. Fondamentalement, cette égalité indique 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 erreur de calcul/d'exécution non terminale dans les listes, car le type d'erreur est polymorphe. Autrement dit, cette égalité rompt déjà dans un langage de programmation comme Haskell sans seq
. Les définitions de fonction suivantes fournissent un contre-exemple de l'équation. Fondamentalement, sur le côté gauche g
"cache" l'erreur.
g _ = True
f _ = [undefined]
Afin de corriger l'équation, g
doit être strict, c'est-à-dire qu'il doit mapper une erreur sur une erreur. Dans ce cas, l'égalité est maintenue.
Si vous ajoutez un opérateur polymorphe seq
, l'équation se rompt à nouveau, par exemple, l'instanciation suivante est un contre-exemple.
g True = True
f (x:y:_) = [seq x y]
Si l'on considère la liste xs = [False, True]
, on a
map g (f [False, True]) = map g [True] = [True]
mais d'autre part
f (map g [False, True]) = f [undefined, True] = [undefined]
Autrement dit, vous pouvez utiliser seq
pour faire dépendre l'élément d'une certaine position de la liste de la définition d'un autre élément de la liste. L'égalité est maintenue si g
est totale. Si vous êtes intéressé par les théorèmes libres, consultez le générateur de théorème gratuit , qui vous permet de spécifier si vous envisagez un langage avec des erreurs ou même un langage avec seq
. Bien que cela puisse sembler moins pertinent sur le plan pratique, seq
interrompt certaines transformations utilisées pour améliorer les performances des programmes fonctionnels, par exemple, foldr
/build
fusion é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
, jetez un œil à Free Theorems in the Presence of seq .
Autant que je sache, on savait qu'un seq
polymorphe rompt certaines transformations, lorsqu'il a été ajouté au langage. Cependant, les autres ont également des inconvénients. Si vous ajoutez une classe de type basée sur seq
, vous devrez peut-être ajouter de nombreuses contraintes de classe de type à votre programme, si vous ajoutez un seq
quelque part au fond. De plus, il n'avait pas été choisi d'omettre seq
car il était déjà connu qu'il y avait des fuites d'espace qui pouvaient être corrigées à l'aide de seq
.
Enfin, je pourrais manquer quelque chose, mais je ne vois pas comment un opérateur seq
de type a -> a
travaillerait. L'indice de seq
est qu'il évalue une expression en tête de forme normale, si une autre expression est évaluée en tête de forme normale. Si seq
a le type a -> a
il n'y a aucun moyen de faire dépendre l'évaluation d'une expression de celle d'une autre expression.
Un autre contre-exemple est donné dans cette réponse - les monades ne satisfont pas aux lois des monades avec seq
et undefined
. Et puisque undefined
ne peut pas être évité dans un langage complet de Turing, celui à blâmer est seq
.