web-dev-qa-db-fra.com

"Quelle partie de Hindley-Milner ne comprends-tu pas?"

Je jure qu'il y avait un t-shirt à vendre comportant les mots immortels:


Quelle partie de

Hindley-Milner

ne comprenez-vous pas ?


Dans mon cas, la réponse serait ... tout ça!

En particulier, je vois souvent une telle notation dans les papiers de Haskell, mais je n’ai aucune idée de ce que cela signifie. Je n'ai aucune idée de quelle branche des mathématiques c'est censé être.

Je reconnais bien sûr les lettres de l'alphabet grec et des symboles tels que "" (ce qui signifie généralement que quelque chose n'est pas un élément d'un ensemble).

D'un autre côté, je n'ai jamais vu "⊢" auparavant ( Wikipedia affirme que cela pourrait signifier "partition" ). Je ne connais pas non plus l'utilisation du vinculum ici. (Habituellement, il s'agit d'une fraction, mais cela ne semble pas comme ici.)

Si quelqu'un pouvait au moins me dire par où commencer à chercher pour comprendre ce que signifie cette mer de symboles, cela serait utile.

824
MathematicalOrchid
  • La barre horizontale signifie que "[ci-dessus] implique [ci-dessous]".
  • S'il y a plusieurs expressions dans [ci-dessus], alors considérez-les anded ensemble; tous les [ci-dessus] doivent être vrais pour garantir le [ci-dessous].
  • : signifie a le type
  • signifie est dans. (De même, signifie "n'est pas en".)
  • Γ est généralement utilisé pour désigner un environnement ​​ou un contexte; dans ce cas, il peut s'agir d'un ensemble d'annotations de type, associant un identifiant à son type. Par conséquent, x : σ ∈ Γ signifie que l'environnement Γ comprend le fait que x a le type σ.
  • peut être lu comme prouve ou détermine. Γ ⊢ x : σ signifie que l'environnement Γ détermine que x a le type σ.
  • , est un moyen de inclure des hypothèses supplémentaires spécifiques dans un environnement Γ.
    Par conséquent, Γ, x : τ ⊢ e : τ' signifie que l'environnement Γ, , avec l'hypothèse supplémentaire dominante que x a le type τ, prouve que e a le type τ'.

Comme demandé: priorité des opérateurs, du plus élevé au plus bas:

  • Opérateurs d'infix et de mixfix spécifiques à la langue, tels que λ x . e, ∀ α . σ et τ → τ', let x = e0 in e1, et des espaces pour l'application de la fonction.
  • :
  • et
  • , (associatif de gauche)
  • espaces séparant plusieurs propositions (associatives)
  • la barre horizontale
627
Dan Burton

Cette syntaxe, même si elle peut paraître compliquée, est en réalité assez simple. L'idée de base vient de la logique formelle: toute l'expression est une implication avec la moitié supérieure étant les hypothèses et la moitié inférieure étant le résultat. Autrement dit, si vous savez que les expressions du haut sont vraies, vous pouvez en conclure que les expressions du bas sont également vraies.

Symboles

Une autre chose à garder à l’esprit est que certaines lettres ont une signification traditionnelle; En particulier, Γ représente le "contexte" dans lequel vous vous trouvez, c'est-à-dire le type d'autres choses que vous avez vues. Donc, quelque chose comme Γ ⊢ ... signifie "l'expression ... lorsque vous connaissez les types de chaque expression dans Γ.

Le symbole signifie essentiellement que vous pouvez prouver quelque chose. Donc, Γ ⊢ ... est une déclaration disant "je peux prouver ... dans un contexte Γ. Ces déclarations sont également appelées jugements de type.

Une autre chose à garder à l’esprit: en mathématiques, tout comme ML et Scala, x : σ signifie que x a le type σ. Vous pouvez le lire comme le x :: σ de Haskell.

Que signifie chaque règle

Donc, sachant cela, la première expression devient facile à comprendre: si nous savons que x : σ ∈ Γ (c’est-à-dire que x a un type σ dans un contexte Γ), alors nous savons que Γ ⊢ x : σ (c'est-à-dire que dans Γ, x a le type σ). Donc, vraiment, cela ne vous dit rien de très intéressant; il vous indique simplement comment utiliser votre contexte.

Les autres règles sont aussi simples. Par exemple, prenez [App]. Cette règle a deux conditions: e₀ est une fonction d'un type τ à un type τ' et e₁ est une valeur de type τ. Maintenant, vous savez quel type vous obtiendrez en appliquant e₀ à e₁! Espérons que ce n'est pas une surprise :).

La prochaine règle a encore une nouvelle syntaxe. En particulier, Γ, x : τ signifie simplement le contexte constitué de Γ et du jugement x : τ. Donc, si nous savons que la variable x a un type de τ et que l'expression e a un type τ', nous connaissons également le type d'une fonction prenant x et retourne e. Cela nous indique simplement quoi faire si nous avons déterminé le type d'une fonction et le type qu'elle renvoie, ce qui ne devrait donc pas être surprenant.

La suivante vous explique simplement comment gérer les instructions let. Si vous savez qu'une expression e₁ a un type τ tant que x a un type σ, puis une expression let qui lie localement x à une valeur de type σ donnera à e₁ un type τ. En réalité, cela vous indique simplement qu'une instruction let vous permet essentiellement de développer le contexte avec une nouvelle liaison, ce qui est exactement ce que fait let!

La règle [Inst] traite du sous-typage. Il indique que si vous avez une valeur de type σ' et qu'il s'agit d'un sous-type de σ ( représente une relation d'ordre partielle), cette expression est aussi de type σ.

La règle finale traite des types généralisants. Un petit aparté: une variable libre est une variable qui n'est pas introduite par une instruction let ou par lambda dans une expression; cette expression dépend maintenant de la valeur de la variable free de son contexte. La règle dit que s’il existe une variable α qui est not "free" dans votre contexte, alors il est prudent de dire que toute expression dont vous connaissez le type e : σ aura ce type pour toute valeur de α.

Comment utiliser les règles

Alors, maintenant que vous comprenez les symboles, que faites-vous avec ces règles? Eh bien, vous pouvez utiliser ces règles pour déterminer le type de valeurs différentes. Pour ce faire, examinez votre expression (par exemple, f x y) et recherchez une règle dont la conclusion (la partie inférieure) correspond à votre déclaration. Appelons la chose que vous essayez de trouver votre "objectif". Dans ce cas, vous devriez regarder la règle qui se termine par e₀ e₁. Une fois que vous avez trouvé cela, vous devez maintenant trouver des règles prouvant tout au-dessus de la ligne de cette règle. Ces choses correspondent généralement aux types de sous-expressions, vous récurez donc essentiellement sur des parties de l'expression. Vous ne le faites que jusqu'à la fin de votre arbre de vérification, ce qui vous donne une preuve du type de votre expression.

Toutes ces règles spécifient donc exactement - et dans le détail mathématique pédantique habituel: P - comment déterminer les types d’expressions.

Cela devrait vous paraître familier si vous avez déjà utilisé Prolog - vous calculez l’arbre de preuve comme un interprète Prolog humain. Il y a une raison pour laquelle Prolog s'appelle "programmation logique"! Ceci est également important car la première façon de me familiariser avec l'algorithme d'inférence H-M a été de l'implémenter dans Prolog. Ceci est en fait étonnamment simple et rend ce qui se passe clair. Vous devriez certainement l'essayer.

Remarque: j'ai probablement fait des erreurs dans cette explication et j'adorerais que quelqu'un les signale. Je vais en parler dans quelques semaines, donc je serai plus confiant alors: P.

319
Tikhon Jelvis

si quelqu'un pouvait au moins me dire par où commencer à chercher pour comprendre ce que signifie cette mer de symboles

Voir " Fondements pratiques des langages de programmation. ", chapitres 2 et 3, sur le style de la logique par le biais de jugements et de dérivations. Le livre entier est maintenant disponible sur Amazon.

Chapitre 2

Définitions inductives

Les définitions inductives sont un outil indispensable dans l’étude des langages de programmation. Dans ce chapitre, nous développerons le cadre de base des définitions inductives et donnerons quelques exemples de leur utilisation. Une définition inductive consiste en un ensemble de règles permettant de dériver jugements, ou assertions, d'une variété de formes. Les jugements sont des déclarations sur un ou plusieurs objets syntaxiques d'un type spécifié. Les règles spécifient les conditions nécessaires et suffisantes pour la validité d'un jugement et en déterminent donc pleinement le sens.

2.1 Jugements

Nous commençons par la notion d'un jugement, ou assertion à propos d'un objet syntaxique. Nous ferons appel à de nombreuses formes de jugement, y compris des exemples tels que:

  • n nat - n est un nombre naturel
  • n = n1 + n2 - n est la somme de n1 et - n2
  • τ type - τ est un type
  • e: τ - l'expression e a le type τ
  • ev - l'expression e a une valeur v

Un jugement déclare qu'un ou plusieurs objets syntaxiques ont une propriété ou sont en relation les uns avec les autres. La propriété ou la relation elle-même est appelée forme de jugement, et le jugement qu'un objet ou des objets ont cette propriété ou cette position dans cette relation est dit être un instance de ce jugement forme. Une forme de jugement est également appelée un prédicat, et les objets constituant une instance sont ses sujets. Nous écrivons aJ pour le jugement affirmant que J est titulaire de a. Lorsqu'il n'est pas important d'insister sur le sujet de l'arrêt, (le texte est coupé ici)

70
Don Stewart

La notation vient de déduction naturelle .

Le symbole s'appelle tourniquet .

Les 6 règles sont très faciles.

La règle Var est une règle plutôt triviale: elle indique que si le type d'identificateur est déjà présent dans votre environnement de type, vous devez en déduire le type que vous venez de extraire de l'environnement tel quel.

La règle App indique que si vous avez deux identificateurs e0 et e1 et que vous pouvez en déduire leurs types, vous pouvez en déduire le type d'application e0 e1. La règle se lit comme ceci si vous savez que e0 :: t0 -> t1 et e1 :: t0 (le même t0!), Alors l'application est bien typée et le type est t1.

Abs et Let sont des règles permettant d'inférer des types pour lambda-abstraction et let-in.

La règle Inst indique que vous pouvez remplacer un type par un type moins général.

48
nponeccop

Comment puis-je comprendre les règles Hindley-Milner?

Hindley-Milner est un ensemble de règles sous la forme calcul séquentiel (pas de déduction naturelle) qui dit que vous pouvez déduire le type (le plus général) d'un programme de la construction du programme sans déclaration de type explicite .

Les symboles et la notation

Tout d'abord, expliquons les symboles

  • ???? est un identifiant (de manière informelle, un nom de variable).
  • : signifie est un type de (informellement, une instance de ou "est-un").
  • ???? (sigma) est une expression qui est une variable ou une fonction.
  • ∈ signifie est un élément de
  • ???? (Gamma) est un environnement.
  • (le signe d'assertion) signifie affirme (ou prouve, mais, dans le contexte, "affirme" lit mieux.)
  • ???? ???? : ???? est donc lu ???? affirme ????, a ????
  • ???? est une instance réelle (élément) de type ???? .
  • ???? (tau) est un type: soit basique, variable ( ???? ), fonctionnel ???? → ???? ' ou produit ? ??? × ???? '
  • ???? → ???? ' est un type fonctionnel où ???? et ???? ' sont des types.
  • ????????. ???? signifie ???? (lambda) est une fonction anonyme qui prend un argument, ???? et retourne une expression, ???? .
  • let ???? = ???? dans ???? signifie dans l'expression, ???? , substitut ???? ???? apparaît.
  • signifie que l'élément antérieur est un sous-type (informellement - sous-classe) du dernier élément.
  • ???? est une variable de type.
  • ????. ???? est un type, des variables d'argument de type ∀ (pour tous), ? ??? , retournant une expression ????
  • free (????) ne signifie pas un élément des variables de type libre de ???? défini dans le contexte externe. (Les variables liées sont substituables.)

Tout ce qui est au-dessus de la ligne est la prémisse, tout ce qui est au-dessous est la conclusion ( Per Martin-Löf )

Ce qui suit sont des interprétations anglaises des énoncés logiques, suivies d’un reformat et d’une explication.

Variable

VAR Logic Diagram

Donné ???? est un type de ???? (sigma), un élément de ???? (Gamma),
conclure ???? affirme ???? est un ????.

En d'autres termes, en ????, nous savons ???? est de type ???? parce que ???? est de type ???? dans ????.

Ceci est fondamentalement une tautologie. Un identifiant est une variable ou une fonction.

Application de fonction

APP Logic Diagram

Donné ???? affirme ???? ₀ est un type fonctionnel et ???? affirme que ???? est un ????
conclure ???? affirme l'application de la fonction ???? ₀ à ???? ₁ est un type ???? '

Pour reformuler la règle, nous savons que la fonction application renvoie le type ???? '. parce que la fonction a le type ???? → ???? ' et obtient un argument de type ????.

Cela signifie que si nous savons qu'une fonction retourne un type et que nous l'appliquons à un argument, le résultat sera une instance du type que nous savons qu'elle renvoie.

Abstraction de fonction

ABS Logic Diagram

Donné ???? et ???? de type ???? affirme ???? est un type, ???? '
conclure ???? affirme une fonction anonyme, ???? de ???? retour d'expression, ???? est de type ???? → ???? '.

Encore une fois, quand on voit une fonction qui prend ???? et retourne une expression ????, nous savons que c'est du type ???? → ???? ' parce que ???? (a ????) affirme que ???? est un ????'.

Si on sait ???? est de type ???? et donc une expression ???? est de type ???? ', alors fonction de ???? expression retournée ???? est de type ???? → ???? '.

Let déclaration de variable

LET Logic Diagram

Donné ???? affirme ????, de type ????, et ???? et ????, de type ????, affirme ???? de type ????
conclure ???? affirme let ???? = ???? ₀ in ???? de type ????

Librement, ???? est lié à ???? en ???? (un ????) car ???? est un ????, et ???? est un ???? qui affirme que ???? est un ????.

Cela signifie que si nous avons une expression ???? c'est un ???? (étant une variable ou une fonction), et un nom, ????, aussi un ????, et une expression ???? ₁ de type ????, alors nous pouvons remplacer ???? par ???? où qu'il apparaisse à l'intérieur de ????.

Instanciation

INST Logic Diagram

Donné ???? affirme ???? de type ???? ' et ????' est un sous-type de ????
conclure ???? affirme ???? est de type ????

Une expression, ???? est de type parent ???? parce que l'expression ???? est le sous-type ???? ', et ???? est le type parent de ???? '.

Si une instance est d'un type qui est un sous-type d'un autre type, il s'agit également d'une instance de ce super-type - le type plus général.

Généralisation

GEN Logic Diagram

Donné ???? affirme ???? est un ???? et ???? n'est pas un élément des variables libres de ????,
conclure ???? assert ????, tapez pour toutes les expressions d'argument ???? en retournant un ???? expression

Donc en général, ???? est tapé ???? pour toutes les variables argument (????) retournant ????, parce que nous savons que ???? est un ???? et ???? n'est pas une variable libre.

Cela signifie que nous pouvons généraliser à un programme d'accepter tous les types d'arguments non liés dans la portée contenue (variables non locales). Ces variables liées sont substituables.

Mettre tous ensemble

Étant donné certaines hypothèses (telles que l'absence de variables libres/indéfinies, un environnement connu), nous connaissons les types de:

  • éléments atomiques de nos programmes (Variable),
  • les valeurs retournées par les fonctions (Function Application),
  • les constructions fonctionnelles (Fonction Abstraction),
  • let bindings (laisser des déclarations variables),
  • types d'instances parents (instanciation), et
  • toutes les expressions (généralisation).

Conclusion

Ces règles combinées nous permettent de prouver le type le plus général d'un programme revendiqué, sans exiger d'annotations de type.

47
Aaron Hall

Il y a deux façons de penser à e: σ. L'un est "l'expression e a le type σ", l'autre est "le couple ordonné de l'expression e et le type σ".

Voir Γ comme la connaissance sur les types d'expressions, implémentée sous la forme d'un ensemble de paires d'expression et de type, e: σ.

Le tourniquet ⊢ signifie que, à partir de la connaissance de gauche, nous pouvons en déduire ce qui se trouve à droite.

La première règle [Var] peut donc être lue:
Si notre connaissance contient le couple e: σ, alors nous pouvons déduire de Γ que e a le type σ.

La deuxième règle [App] peut être lue:
Si nous pouvons déduire que e_0 est de type τ → τ ', et que nous pouvons déduire que e_1 est de type τ, alors nous pouvons déduire que e_0 e_1 est de type τ'.

Il est courant d'écrire, e: σ au lieu de Γ ∪ {e: σ}.

La troisième règle [Abs] peut donc être lue:
Si nous avons prolongé de Γ avec x: τ peut déduire que e est de type τ ', alors de Γ pouvons en déduire que λx.e est de type τ → τ'.

La quatrième règle [Let] est laissée en exercice. :-)

La cinquième règle [Inst] peut être lue:
Si nous pouvons déduire de e que e a le type σ ', et que σ' est un sous-type de σ, alors nous pouvons en déduire que e a le type σ.

La sixième et dernière règle [Gen] peut être lue:
Si nous pouvons déduire de e que e est de type σ et que α n'est pas une variable de type libre dans aucun des types de Γ, nous pouvons alors en déduire que e est de type ∀α σ.

16
Per Persson