J'expérimente des systèmes de type pur dans le cube lambda de Barendregt, en particulier avec le plus puissant, le Calcul des constructions. Ce système a des sortes *
et BOX
. Pour mémoire, j'utilise ci-dessous la syntaxe concrète de l' Morte
outil https://github.com/Gabriel439/Haskell-Morte-Library qui est proche du calcul lambda classique.
Je vois que nous pouvons émuler des types inductifs par une sorte de codage de type Church (alias isomorphisme Boehm-Berarducci pour les types de données algébriques). Pour un exemple simple, j'utilise type Bool = ∀(t : *) -> t -> t -> t
avec les constructeurs True = λ(t : *) -> λ(x : t) -> λ(y : t) -> x
et False = λ(t : *) -> λ(x : t) -> λ(y : t) -> y
.
Je vois que le type de fonctions au niveau du terme Bool -> T
est isomorphe à des paires de type Product T T
avec une Product = λ(A : *) -> λ(B : *) -> ∀(t : *) -> (A -> B -> t) -> t
paramétricité modulo classique au moyen d'une fonction if : Bool -> λ(t : *) -> t -> t -> t
qui est en fait l'identité.
Toutes les questions ci-dessous porteront sur les représentations des types dépendants Bool -> *
.
Je peux diviser
D : Bool -> *
en paire deD True
etD False
. Existe-t-il la manière canonique de créer àD
nouveau? Je veux reproduire l'isomosphismeBool -> T = Product T T
par un analogue de fonctionif
au niveau du type mais je ne peux pas écrire cette fonction aussi simple qu'originalif
car nous ne pouvons pas passer des types dans des arguments comme des types.J'utilise une sorte de type inductif avec deux constucteurs pour résoudre la question (1). La description de haut niveau (style Agda) est le type suivant (utilisé à la place du niveau de type
if
)data BoolDep (T : *) (F : *) : Bool -> * where DepTrue : T -> BoolDep T F True DepFalse : F -> BoolDep T F False
avec l'encodage suivant dans PTS / CoC:
λ(T : *) -> λ(F : *) -> λ(bool : Bool ) -> ∀(P : Bool -> *) -> ∀(DepTrue : T -> P True ) -> ∀(DepFalse : F -> P False ) -> P bool
Mon encodage ci-dessus est-il correct?
Je peux écrire les constructeurs pour
BoolDep
comme ce code pourDepTrue : ∀(T : *) -> ∀(F : *) -> T -> BoolDep T F True
:λ(T : *) -> λ(F : *) -> λ(arg : T ) -> λ(P : Bool -> *) -> λ(DepTrue : T -> P True ) -> λ(DepFalse : F -> P False ) -> DepTrue arg
mais je ne peux pas écrire la fonction inverse (ou toute fonction dans le sens inverse). C'est possible? Ou devrais-je utiliser une autre représentation pour BoolDep
produire un isomorphisme BoolDep T F True = T
?
la source
Réponses:
Vous ne pouvez pas faire cela en utilisant l'encodage traditionnel de l'Église pour
Bool
:... car vous ne pouvez pas écrire une fonction (utile) de type:
La raison pour laquelle, comme vous l'avez noté, est que vous ne pouvez pas passer
*
comme premier argument à#Bool
, ce qui signifie que les argumentsTrue
etFalse
peuvent ne pas être des types.Il existe au moins trois façons de résoudre ce problème:
Utilisez le calcul des constructions inductives. Ensuite, vous pouvez généraliser le type de
#Bool
:... et vous instancierez
n
à1
, qui signifie que vous pouvez passer*₀
comme second argument, qui de type vérification parce que:... vous pouvez alors utiliser
#Bool
pour sélectionner entre deux types.Ajoutez un tri supplémentaire:
Ensuite, vous définiriez un
#Bool₂
type distinct comme celui-ci:Il s'agit essentiellement d'un cas particulier du Calcul des constructions inductives, mais produit un code moins réutilisable car maintenant nous devons conserver deux définitions distinctes de
#Bool
, une pour chaque type que nous souhaitons prendre en charge.Encode
#Bool₂
directement dans le calcul des constructions comme:Si le but est de l'utiliser directement dans une version non modifiée,
morte
seule l'approche # 3 fonctionnera.la source
#Bool₁
à#Bool₂