Pourquoi Haskell est-il incapable d'éviter une évaluation répétée sans la restriction du monomorphisme?

14

Je viens juste de terminer Learnyouahaskell l'autre jour, et j'essayais de donner un sens à la restriction du monomorphisme, comme décrit par le Haskell Wiki . Je pense que je comprends comment le MR peut empêcher des évaluations répétées, mais je ne vois pas pourquoi ces évaluations répétées ne peuvent pas être évitées par des moyens beaucoup plus simples.

L'exemple spécifique que j'ai en tête est celui utilisé par le wiki:

f xs = (len,len)
  where
    len = genericLength xs

genericLengthest de type Num a => [b] -> a.

Évidemment, il genericLength xssuffit de calculer une seule fois pour évaluer (len,len), car c'est la même fonction avec les mêmes arguments. Et nous n'avons pas besoin de voir d'invocations fpour le savoir. Alors pourquoi Haskell ne peut-il pas faire cette optimisation sans introduire une règle comme le MR?

La discussion sur cette page wiki me dit que cela a quelque chose à voir avec le fait qu'il Nums'agit d'une classe de caractères plutôt que d'un type concret, mais même ainsi, ne devrait-il pas être évident au moment de la compilation qu'une fonction pure retournerait la même valeur- -et donc le même type concret de Num - quand on lui donne deux fois les mêmes arguments?

Ixrec
la source

Réponses:

11

C'est le même nom de fonction, avec les mêmes arguments, mais des types de retour et des implémentations potentiellement différents, car il est polymorphe. Cela signifie que s'il est appelé dans un contexte qui attend un (Int, MyWeirdCustomNumType)type de retour, il doit l'évaluer deux fois, car l'implémentation de (+)in Intest complètement différente de l'implémentation de (+)in MyWeirdCustomNumType. Vous devez exécuter un code physiquement différent à un moment donné.

C'est pourquoi il importe que ce Numsoit une classe de type. Cela signifie que vous avez différentes implémentations pour différents types. Cela signifie également que si se ftrouve dans une bibliothèque, il ne sait pas au moment de la compilation toutes les combinaisons de types qu'il pourrait avoir besoin de renvoyer.

Donc, oui, vous devez voir les invocations de fpour savoir quels types de retour utiliser. La plupart du temps, vous vous attendez à ce qu'ils soient identiques, c'est pourquoi ils mettent la restriction du monomorphisme par défaut. Il peut être désactivé pour les rares occasions où vous ne le faites pas. Dans la pratique, les programmeurs n'ont généralement pas tendance à laisser ce type de situations à l'inférence de type.

Karl Bielefeldt
la source
Je n'avais pas envisagé la possibilité de f [] :: (Int, Float). Maintenant, c'est parfaitement logique. Je vous remercie.
Ixrec
1
It's the same function name, with the same arguments, but potentially different return types and implementations, because it's polymorphic.Je pense que la meilleure façon de voir les choses est que l'instance de typeclass est effectivement un argument supplémentaire genericLengthet que le compilateur n'est pas convaincu que ce sont les mêmes arguments dans les deux appels.
Doval
Question secondaire rapide. Si MonomorphismRestriction est désactivé, mais que vous avez ensuite fait quelque chose comme: a = uncurry (==) $ f [1, 2, 3]Serait-il capable d'optimiser ce site d'appel pour ne vérifier que la durée d' [1, 2, 3]une fois? Si oui, alors je suis vraiment confus quant à ce que la restriction du monomorphisme vous achète réellement, sinon alors pourquoi pas?
point