Je sais comment certaines occurrences négatives peuvent définitivement être mauvaises:
data False
data Bad a = C (Bad a -> a)
selfApp :: Bad a -> a
selfApp (x@(C x')) = x' x
yc :: (a -> a) -> a
yc f = selfApp $ C (\x -> f (selfApp x))
false :: False
false = yc id
Cependant, je ne sais pas si:
tous les types inductifs avec des événements négatifs peuvent tourner mal;
dans l'affirmative, il existe un moyen mécanique connu de le faire;
Par exemple, je me suis battu en essayant de faire mal ce type:
type Not a = a -> False
data Bad2 a = C2 (Bad2 (Not a) -> a)
Tout pointeur vers la littérature sur ce sujet serait apprécié.
lo.logic
type-theory
soundness
Ptival
la source
la source
Réponses:
La raison de l'interdiction des occurrences négatives peut être comprise par analogie avec le théorème de Knaster-Tarski. Ce théorème dit que
Dans la théorie des modèles traditionnels, les réseaux peuvent être considérés comme des propositions, et la relation d'ordre p ≤ q peut être comprise comme une implication (c'est-à-dire que la vérité de q est impliquée par la vérité de p ).L p ≤ q q p
Lorsque nous passons de la théorie des modèles à la théorie des preuves, les réseaux se généralisent en catégories. Les types peuvent être considérés comme les objets d'une catégorie , et une carte e : P → Q représente une preuve que Q peut être dérivée de Q .C e : P→ Q Q Q
Lorsque nous essayons d'interpréter des types définis par des équations récursives, ee, , la chose évidente à faire est de chercher une généralisation du théorème de Knaster-Tarski. Ainsi, au lieu d'une fonction monotone sur un réseau, nous savons que nous voulons unfoncteur F : C → C , qui envoie des objets aux objets, mais généralise la condition de monotonie afin que chaque carte e : P → Q obtienne une carte F ( e ) : F ( P ) → F ( Q ) (avec les conditions de cohérence que F envoie des identités aux identités et préserve les compositions pour que FN =μα.1 + α F: C → C e : P→ Q F( e ) : F( P) → F( Q ) F ).F( g∘ f) = F( g) ∘ F( f)
Donc, si vous voulez un type de données inductif , vous devez également fournir une action fonctorielle aux termes de l'opérateur de type F afin de vous assurer que le point fixe souhaité existe. La condition de positivité stricte dans Agda et Coq est uneconditionsyntaxiquequi implique cettecontraintesémantique. En gros, il dit que si vous construisez un opérateur de type à partir de sommes et de produits, vous pouvez toujours faire cuire l'action fonctorielle, et donc tout type formé de cette manière devrait avoir un point fixe.μ α .F( α ) F
Dans les langages de type dépendant, vous avez également des types indexés et paramétrés, donc votre tâche réelle est plus compliquée. Bob Atkey (qui a blogué à ce sujet ici et ici ) me dit qu'un bon endroit pour chercher l'histoire est:
Comme le note Andrej, fondamentalement, si une occurrence négative est correcte ou non, cela dépend de votre modèle de théorie des types. Fondamentalement, lorsque vous avez une définition récursive, vous recherchez un point fixe, et il y a beaucoup de théorèmes à point fixe en mathématiques.
Personnellement, j'ai beaucoup utilisé le théorème du point fixe de Banach, qui dit que si vous avez une fonction strictement contractuelle sur un espace métrique, alors il a un point fixe unique. Cette idée a été introduite dans la sémantique par (IIRC) Maurice Nivat, et a été largement étudiée par America et Rutten, et a été récemment reliée par Birkedal et ses collaborateurs à une technique opérationnelle populaire appelée "indexation par étapes".
Cela donne lieu à des théories de types où les occurrences négatives dans les types récursifs sont autorisées, mais uniquement lorsque les occurrences négatives se produisent sous un constructeur de type "garde" spécial. Cette idée a été introduite par Hiroshi Nakano, et le lien avec le théorème de Banach a été établi à la fois par moi-même et Nick Benton, ainsi que Lars Birkedal et ses coauteurs.
la source
Parfois, vous pouvez résoudre des équations récursives "par chance".
Conclusion: il existe deux solutions, le type vide (que vous avez appelé
False
) et le type d'unité()
.Integer
(Integer -> Bool) -> Bool
la source
Il est difficile d'ajouter quoi que ce soit aux explications d'Andrej ou de Neel, mais je vais essayer. Je vais essayer d'aborder le point de vue syntaxique, plutôt que de découvrir la sémantique sous-jacente, parce que l'explication est plus élémentaire et que je donnerai une réponse plus simple à votre question.
La référence cruciale est la suivante:
Mendler, N. (1991). Types inductifs et contraintes de type dans le calcul lambda du second ordre. Je n'ai pas trouvé de référence en ligne, je le crains. Les déclarations et les preuves peuvent cependant être trouvées dans la thèse de doctorat de Nax (une lecture fortement recommandée!).
et donc
Bien sûr, vous ne travaillez pas avec des types définis équativement mais avec des constructeurs , c'est-à-dire que vous avez
plutôt qu'une stricte égalité. Cependant, vous pouvez définir
ce qui est suffisant pour que ce résultat continue de tenir:
Dans votre deuxième exemple, les choses sont un peu plus délicates, car vous avez quelque chose comme
avec
Il serait facilement résolu si Haskell permettait de telles définitions de type:
Dans ce cas, vous pouvez créer un combinateur en boucle exactement de la même manière qu'auparavant. Je suppose que vous pouvez réaliser une construction similaire (mais plus complexe) en utilisant
Le problème ici est que pour construire un isomorphisme
vous devez faire face à une variance mixte.
la source