Y a-t-il une bonne raison pour laquelle «Deleteby» n'a pas son type le plus général?

StackOverflow https://stackoverflow.com/questions/9004937

  •  14-11-2019
  •  | 
  •  

Question

Le rapport sur la langue Haskell 2010 indique dans l'article 20.10.1.1 que:

deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]

En fait, la mise en œuvre dans le Bibliothèque GHC permettrait

deleteBy :: (b -> a -> Bool) -> b -> [a] -> [a]

mais restreint en fait le type à l'ancien avec l'annotation.

Par conséquent, on ne peut pas dire, par exemple:

foo = deleteBy fsteq 42 [(43, "foo"), (44, "bar"), (42, "baz")] where
    fsteq a (b,_) = a == b

car Int n'est pas le même que (Int, String).

Y a-t-il une bonne raison à cela?

La raison pour laquelle je demande, c'est que s'il n'y a aucune bonne raison à cela, j'inclurais deleteBy avec le type plus général dans le Freer port de data.list que je fais actuellement. Mais peut-être que je néglige quelque chose?

Edit: Comme l'a souligné @hammar, cela s'applique à d'autres xxxPar fonctions également.

Était-ce utile?

La solution

Généralisation du type de deleteBy Viole la norme de manière très pratique: les programmes Haskell parfaitement valides deviennent invalides, grâce à une surcharge non résolue.

Voici une démonstration:

class (Num a) => Magic a where
  magic :: a -> Bool

sameMagic :: (Magic a, Magic b) => a -> b -> Bool
sameMagic a b = magic a == magic b

test :: (Magic a) => [a]
test = deleteBy sameMagic 42 [1234]

Dans Haskell, ce programme est parfaitement bien type; deleteByle type restreint de 42 est garanti d'avoir le même type que le 1234. Avec le généralisé deleteBy, ce n'est pas le cas, et donc le type de 42 est ambigu, rendant le programme invalide. (Si vous voulez un exemple moins artificiel, considérez une fonction qui compare deux Integral valeurs avec toInteger.)

Donc, il n'y a peut-être aucune bonne raison pour ce type restreint (bien que si deleteBy doit être généralisé, je préférerais la version de Hammar à votre proposition), mais la généralisation Est-ce que Violez la norme et peut casser des programmes valides.

Autres conseils

Je suppose que c'est pour la symétrie avec l'autre xxxBy les fonctions. Cependant, votre type est toujours inutilement spécifique. Je préférerais ça.

deleteBy :: (a -> Bool) -> [a] -> [a]

Vous pouvez ensuite écrire votre exemple en utilisant une application partielle:

foo = deleteBy (fsteq 42) [(43, "foo"), (44, "bar"), (42, "baz")] where
    fsteq a (b,_) = a == b

Ingo,

Dans votre question initiale, il semble que vous vous demandez pourquoi le rapport Haskell spécifie supprimer comme ça. Donc, s'il n'y a pas de raison forte, vous pouvez utiliser une définition différente dans Frege (ce qui implique que vous ne vous souciez pas de la conformité du rapport Haskell).

Comme le dit Hammar, c'est comme les autres fonctions xxxby: effacer les usages (==), supprimer prend un prédicat qui est comme (==): de type (a -> a -> bool) et supposé être une relation d'équivalence. Bien que le système de type ne puisse pas vérifier si le prédicat est vraiment une relation d'équivalence, c'est le contrat de fonction. Il est donc très facile de comprendre ce que signifie xxxby si vous savez ce que signifie xxx. Et ce n'est peut-être pas le cas pour Deleteby, mais dans certains cas, une implémentation peut être optimisée en vertu de l'hypothèse, le prédicat a les propriétés spécifiées (relation d'équivalence ou ordre total, etc.).

Mais dans votre commentaire à la réponse de Hammar, vous vous demandez si la mise en œuvre la plus générale violerait le rapport. Eh bien, si le type est différent, c'est littéralement une violation, non? Étant donné que les programmes qui ne devraient pas se compiler en fonction du rapport seront acceptés par votre compilateur. Il donne donc naissance à un problème de portabilité: si votre code utilise la version plus générale, il peut ne pas se compiler sur une autre implémentation conforme à la spécification. En outre, il supprime l'exigence de relation d'équivalence.

Donc, si vous voulez une fonction plus générale, pourquoi ne pas simplement définir une autre fonction avec un nom différent? Par exemple, deleteif.

(Je voulais commenter la réponse de Hammar, mais je ne peux pas donc je l'ai écrit ici.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top