Il y a plus d'un an, j'ai posé la question Comment utiliser un proxy à Haskell , et depuis lors, j'ai un Petite quantité d'utilisation de l'extension de HankNTypes GHC. Le problème, c'est chaque fois que j'essaie de travailler avec elle, je me retrouve avec des messages d'erreur bizarre et un piratage autour du code jusqu'à ce qu'ils disparaissent. Ou bien j'abandonne.
évidemment, je ne comprends pas vraiment le polymorphisme de rang supérieur dans Haskell. Pour essayer de résoudre ce problème, j'ai décidé de descendre jusqu'aux exemples les plus simples que je pourrais, de tester toutes mes hypothèses et de voir si je pouvais me procurer un moment d'EUREKA. P>
Première hypothèse - La raison du polymorphisme de rang élevé ISN La fonctionnalité Standard Haskell 98 (ou 2010?) Est-ce que cela, à condition que vous acceptiez quelques restrictions évidentes que beaucoup de programmeurs ne remarqueraient même pas, ce n'est pas nécessaire. Je peux définir les fonctions polymorphes de rang 1 et de rang 2 qui sont, à première vue, équivalentes. Si je les chargez dans GHCI et que je les appelez avec les mêmes paramètres, ils donneront les mêmes résultats. P>
Exemple simple Fonctions ... P>
*Main> ([1,2,3] :: [Int]) :: [a] <interactive>:15:2: Couldn't match type `a1' with `Int' `a1' is a rigid type variable bound by an expression type signature: [a1] at <interactive>:15:1 Expected type: [a] Actual type: [Int] In the expression: ([1, 2, 3] :: [Int]) :: [a] In an equation for `it': it = ([1, 2, 3] :: [Int]) :: [a] *Main>
3 Réponses :
Pensons à ce que le type de Le premier argument à Ainsi, afin de donner quelque chose comme un argument à contraste avec Rank2 code> signifie.
Rank2 code> doit être quelque chose de type FORTEZ A. [a] code>. Le
FORALL code> étant le plus à l'extérieur signifie que quiconque obtient em> une telle valeur peut choisir leur choix de
A code>. Pensez-y comme prenant un type comme un argument supplémentaire. P>
Rank2 code>, il doit être une liste dont les éléments peuvent être de tout type em> que l'implémentation interne de
Rank2 code> pourrait vouloir. Comme il n'ya aucun moyen de conjurer des valeurs d'un tel type arbitraire, les seules entrées possibles sont les seules entrées possibles sont
[] code> ou des listes contenant
non défini code>. P>
Rank1b CODE>: P>
rank2c :: (forall a. [a -> a]) -> Bool
rank2c x = null x
Donc - passer tout ce qui est utile à cette fonction et faire quelque chose d'utile avec ce paramètre, je vais certainement avoir besoin d'une sorte de contrainte de frappe de caractères - peut-être Rank2 :: (Forall a. Num A => [A] ) -> bool code>?
@ Steve314 à peu près - avec (Forall a. [A]) -> BOOL CODE>, vous ne pouvez rien faire avec les éléments. Vous pouvez vérifier les choses sur la longueur, mais c'est à ce sujet.
OK - Je suis un peu surpris parce que la fonction null code> ne se soucie évidemment pas des types des éléments de la liste. Cependant, alors que j'ai eu l'idée que le paramètre polymorphe a un ensemble de types possibles et que cela a oublié que la callee décide du type à interpréter comme (bien que maintenant vous en parlez, je pense que cela a été mentionné dans une réponse à mon précédent question).
@ Steve314 oui, null code> ne se soucie pas des types des éléments. C'est pourquoi le général
Rank1x code> fonctionne. Pour
Rank2 CODE>, vous avez spécifié un type beaucoup plus restrictif, mais vous n'avez pas fourni d'argument qui pourrait avoir le type restrictif
Forall A. [a] code>. Une fois que vous avez spécifié un type moins général que le type inféré, vous limitez les cas d'utilisation possibles de votre fonction.
@Daniel Fischer - OK, il semble que je dois repenser quelques idées que j'avais sur le polymorphisme de rang 1 ainsi que le rang 2.
Merci beaucoup pour le Rank2C code> exemple. Je vais juste jeter dans un
rank2d :: (Forall a. [A] -> [a]) -> BOOL CODE> Pour montrer que je pense que je commence à obtenir cette eureka - la Callee peut souvent utilement décidé du type d'un paramètre lorsque ce paramètre est une fonction - un paramètre approprié est simplement une fonction polymorphe ordinaire (rang 1) qui pourrait par exemple Réorganisez les valeurs dans une structure sans attentionné quel type de valeurs il réarrange
@ Steve314: Oui. Tout ce que vous pouvez écrire comme une définition polymorphe de haut niveau pourrait avoir un sens ici. La seule valeur définie avec type Forall a. [a] code> est
[] code>, mais
Forall a. [a] -> [a] code> pourrait être un tas de choses telles que
inverse code>,
cycle code>,
goutte 3 code>,
const [] code> ... Le point de clé ici est celui avec
Rank2C code> ou
Rank2D code>, le choix d'un type
a Code> et une certaine valeur avec ce type sont donnés comme des arguments. Dans votre (code> Rank2 code> d'origine, seul le type était un "argument", mais il s'attendait à une sortie de ce type.
Comparons les signatures de type code> explicitez code> FORAC code> Signatures:
>:t [] [] :: [a]
À ce stade, je n'essaie pas de faire quelque chose d'utile avec un rang supérieur polymorphisme. Le point ici est simplement de pouvoir appeler le rang2 fonctionner avec une liste non vide et comprendre pourquoi cela ne fonctionne pas exactement la même chose que les autres fonctions. Je veux continuer à comprendre cette étape par étape moi-même, mais maintenant je suis tout à fait coincé. p>
Je ne suis pas si sûr que le polymorphisme haut rang est ce que vous pensez que c'est. Je pense que le concept ne fait que sens en ce qui concerne les types de fonctions. P>
par exemple: p>
xxx pré> nous dit, que
inverse code> et
queue code> travail indépendamment du type de l'élément de liste. Maintenant, compte tenu de cette fonction: p>
xxx pré> Quel est le type de
foo code>? L'inférence de HM standard ne peut pas trouver le type. Spécifiquement, il ne peut pas déduire le type de
f code>. Nous devons aider le vérificateur de type ici et promettre que nous n'acceptons que des fonctions qui ne se soucient pas des éléments de liste des éléments de la liste: p>
xxx pré> nous pouvons p>
Xxx pré> et donc avoir un type de rang-2 utilisable. Notez que la signature de type interdit de passer quelque chose comme: p>
xxx pré> car la fonction transmise n'est pas totalement indépendante du type d'élément: il nécessite des éléments numériques. P> BlockQuote>
J'ai commenté avant - les supprima parce qu'ils ont suggéré un nouveau i> malentendu que je préférerais ne pas infecter d'autres personnes. Néanmoins, j'ai accepté, j'avais mal compris le polymorphisme de rang 2 et cela a été aidé par un malentendu du polymorphisme de rang 1 entraînant une pièce d'une fausse analogie avec des modèles C ++. Fondamentalement, une fonction de modèle C ++ spécifie tout un tas de fonctions monomorphes - vous devez savoir lequel l'appellera. Je comprends maintenant que dans Haskell une fonction polymorphe n'est qu'une seule fonction - le type réel doit être corrigé d'une manière ou d'une autre, mais vous n'avez pas à choisir un particulier ...
Mise en œuvre monomorphe pour l'appeler.
De plus, si vous souhaitez utiliser un polymorphisme de rang supérieur avec des données (par opposition aux fonctions), tôt ou tard, vous aurez également besoin de un impréditpolymorphisme code>, qui est une boule de cire complète. En grande partie parce que GHC ne l'appuie pas entièrement à ce moment-là.
"Pour être honnête, j'étais un peu surpris que les fonctions
RANK1A code> et
RANK1B CODE> ont fonctionné dans ce cas." GHCI applique plus de type plus vaste de défaut que GHC: il donne la liste le type
[()] code>. Vous auriez une erreur de type normalement lors de la compilation.
@ Dave4420: Je ne pense pas que tu voudrais. La défaillance ne s'applique que lorsque le type est contraint d'une manière ou d'une autre, mais ici, il est
Forall A. [a] code> (et le résultat est
bool code>, de sorte que cela ne nécessite pas non plus).
@Vitus Non, GHCI a plus de règles de défaillance que cela. Dave4420 est parfaitement correct. Il fait défaut le type à, en fonction de la version de GHC,
() code> ou
tout code>. Ceci est juste quelque chose que GHCI fait pour essayer de vous laisser exécuter des expressions sans fournir de signatures de type.
@CARL: Oui, je suis conscient que GHCI a plus de règles défaillantes, mais aucune de ces informations ne s'applique ici. Signaler stipule que seules les variables de type ambiguë sont en défaut . Par cette définition,
Forall a. [a] code> est le type précis de préfects qui n'a pas besoin de défaut de défaut, GHCI détend ensuite les classes de type pouvant participer à la défaillance et permet également aux types d'être en panne sur
() code>. Et en effet, GHCI est assez cool sur
Forall a. [a] code>.
tout code> est un détail de mise en œuvre.
Oui, @vitus est correct ici. Les types entièrement polymorphes n'ont pas besoin de défaut parce que ce n'est pas ce qu'ils sont. Le type
tout code> est principalement un espace réservé à GHC utilise en interne pour instancier des types non pertinents à quelque chose de concret lors de la compilation. La défaillance n'est nécessaire que pour les variables de type avec une contrainte de classe de type, car le choix de l'instance fait i> matière.