Qu'est-ce que existentielle type?
J'ai lu l'article de Wikipédia Existentielle types. Je compris qu'ils sont appelés à existentielle types en raison de l'opérateur existentiel (∃). Je ne suis pas sûr de ce que le point de il est, cependant. Quelle est la différence entre
T = ∃X { X a; int f(X); }
et
T = ∀x { X a; int f(X); }
?
- Cela peut être un bon sujet pour programmers.stackexchange.com. programmers.stackexchange.com
Vous devez vous connecter pour publier un commentaire.
Quand quelqu'un définit un type universel
∀X
qu'ils disent: vous pourrez Vous connecter quel que soit le type que vous voulez, je n'ai pas besoin de savoir quelque chose au sujet du type d'en faire mon métier, je ne vais le consulter de manière opaque commeX
.Quand quelqu'un définit existentielle type
∃X
qu'ils disent: je vais l'utiliser quel que soit le type que je veux ici; vous ne savez rien sur le type, vous ne pouvez donc le consulter de manière opaque commeX
.Universelle types vous permettent d'écrire des choses comme:
La
copy
fonction n'a aucune idée de ce queT
sera réellement, mais il n'a pas besoin d'.Existentielle types serait vous permettent d'écrire des choses comme:
Chaque machine virtuelle de mise en œuvre de la liste peut avoir un avis différent du bytecode type. Le
runAllCompilers
fonction n'a aucune idée de ce que le pseudo-code de type est, mais il n'est pas nécessaire; il n'est de relayer le bytecode deVirtualMachine.compile
àVirtualMachine.run
.Java de type de caractères (ex:
List<?>
) sont très limitées forme existentielle types.Mise à jour: Oublié de mentionner que vous pouvez sorte de simuler existentielle avec des types universels. Tout d'abord, l'enveloppe de votre type universel pour masquer le paramètre de type. Deuxièmement, inversion de contrôle (cela permet de swaps de le "vous" et "I" dans les définitions ci-dessus, qui est la principale différence entre existentiels et universels).
Maintenant, nous pouvons avoir la
VMWrapper
appeler notre propreVMHandler
qui est universellement tapéhandle
fonction. L'effet net est le même, notre code a pour traiterB
opaque.Un exemple VM mise en œuvre:
List<∃B:VirtualMachine<B>> vms
oufor (∃B:VirtualMachine<B> vm : vms)
. (Puisque ce sont des types génériques, ne pourriez-vous pas avoir utilisé de Java?
des caractères génériques au lieu de "self-made" de la syntaxe?) Je pense qu'il peut aider d'avoir un exemple de code où aucun des types génériques tels que∃B:VirtualMachine<B>
sont en cause, mais plutôt un "droit"∃B
, parce que les types génériques sont facilement associés universelle types après votre première exemples de code.∃B
d'être explicite sur l'endroit où la quantification qui se passe. Avec la syntaxe générique le quantificateur est implicite (List<List<?>>
signifie en fait∃T:List<List<T>>
et pasList<∃T:List<T>>
). Aussi, explicite la quantification permet de consulter le type (j'ai modifié l'exemple à prendre avantage de cela, en stockant le code binaire de typeB
dans une variable temporaire).List<List<?>>
signifieList<∃T:List<T>>
. Il n'y a aucun moyen en Java pour spécifier∃T:List<List<T>>
. LerunAllCompilers
fonctionne parce que la limitation arrive à être bon dans ce cas.∃B:VirtualMachine<B>
?VirtualMatchine<B>
, et je supposeB
peut être n'importe quoi." Type universel∀B:VirtualMatchine<B>
permet de dire "j'ai une valeur qui, pour n'importe quel type je branche commeB
, est valideVirtualMachine<B>
.∃x. F(x)
est une paire(x, value)
: une paire d'un type et une valeur. Une valeur de type universel est une fonction de types de valeurs. La connexion est très faible dans certaines langues, mais avec des types dépendants (lorsque les valeurs et les types de mélange), il devient important.fn Foo(): ∃VirtualMachine
Une valeur de existentielle type comme
∃x. F(x)
est une paire contenant certains typex
et un valeur du typeF(x)
. Tandis qu'une valeur d'un type polymorphe comme∀x. F(x)
est un fonction qui prend un certain type dex
et produit une valeur de typeF(x)
. Dans les deux cas, le type de ferme sur un certain type constructeurF
.Note que ce point de vue les mélanges de types et de valeurs. La preuve existentielle est un type et une valeur. L'universel de la preuve est toute une famille de valeurs indexées par type (ou d'une correspondance entre les types de valeurs).
De sorte que la différence entre les deux types que vous avez spécifié est comme suit:
Cela signifie: Une valeur de type
T
contient un type appeléX
, une valeura:X
, et une fonctionf:X->int
. Un producteur de valeurs de typeT
fait le choix tout type deX
et un consommateur ne peut pas savoir quelque chose au sujet deX
. Sauf qu'il y a un exemple de ce qu'il appellea
et que cette valeur peut être transformé en unint
en le donnant àf
. En d'autres termes, une valeur de typeT
sait comment produire uneint
en quelque sorte. Eh bien, nous pourrions éliminer les intermédiaires de typeX
et dire:Universellement quantifiée est un peu différent.
Cela signifie: Une valeur de type
T
peut être donné à n'importe quel typeX
, et il va produire une valeura:X
, et une fonctionf:X->int
n'importe quelX
est. En d'autres termes: un consommateur de valeurs de typeT
pouvez choisir n'importe quel type deX
. Et, un producteur de valeurs de typeT
ne pouvez pas savoir quoi que ce soit surX
, mais il doit être capable de produire une valeura
pour tout choix deX
, et être en mesure de tourner une telle valeur dans unint
.Évidemment la mise en œuvre de ce type est impossible, car il n'existe pas de programme qui peut produire une valeur de tous les types imaginables. À moins que vous permettez à des absurdités comme
null
ou de fond.Depuis existentielle est une paire, existentielle argument peut être converti en un universel via nourrissage.
est la même chose que:
Le premier est un de rang 2 existentielle. Ceci conduit à considérer une propriété utile:
Il existe un algorithme standard pour le tournage de existentiels dans des universaux, appelé Skolemization.
Je pense que cela fait sens pour expliquer existentielle types avec universels, car les deux concepts sont complémentaires, c'est à dire l'un est la "face" de l'autre.
Je ne peut pas répondre à tous les détails à propos existentielle types (tels que de donner une définition exacte, la liste de toutes les utilisations possibles, leur relation avec les types de données abstraites, etc.) parce que je suis tout simplement pas suffisamment de connaissance pour que. Je vais démontrer (en utilisant Java) ce cette HaskellWiki article les états à être le principal effet de l'existentiel types:
Exemple set-up:
Au pseudo-code n'est pas tout à fait valable Java, même s'il serait assez facile de résoudre ce problème. En fait, c'est exactement ce que je vais faire dans cette réponse!
Permettez-moi de sort pour vous. Nous sommes à la définition...
récursive de type
Tree<α>
qui représente un nœud dans un arbre binaire. Chaque nœud stocke unevalue
de certains type de α et a des références à l'optionleft
etright
les sous-arbres du même type.une fonction
height
qui renvoie la distance la plus longue de toute feuille de nœud à nœud racinet
.Maintenant, passons au-dessus de pseudo-code pour
height
dans la bonne syntaxe Java! (Je vais continuer à omettre certains réutilisable par souci de concision, tels que l'orientation de l'objet et de l'accessibilité des modificateurs.) Je vais vous montrer deux solutions possibles.1. Universal type de solution:
La plus évidente de la solution est de simplement faire
height
générique en introduisant le paramètre de type α dans sa signature:Cela vous permettra de déclarer des variables et créer des expressions de type α à l'intérieur de cette fonction, si vous vouliez. Mais...
2. Existentielle type de solution:
Si vous regardez notre méthode, vous remarquerez que nous ne sommes pas réellement accès à, ou de travailler avec, quoi que ce soit de type α! Il n'y a pas des expressions du type, ni toutes les variables déclarées avec ce type... alors, pourquoi avons-nous les faire
height
générique à tous? Pourquoi ne pouvons-nous pas tout simplement oublier α? Comme il s'avère, nous pouvons:Comme je l'ai écrit au début de cette réponse, existentiels et universels sont complémentaires /double dans la nature. Ainsi, si le type universel solution était de faire
height
plus générique, alors nous devrions nous attendre que existentielle types ont l'effet inverse: c'est moins générique, à savoir en se cachant/retrait du paramètre de type α.En conséquence, vous ne pouvez plus consulter le type de
t.value
dans cette méthode, ni manipuler des expressions de ce type, car aucun identifiant n'a été lié à elle. (Le?
générique est un jeton spécial, pas un identificateur qui "capture" d'un type.)t.value
est effectivement devenu opaque; peut-être la seule chose que vous pouvez toujours faire avec il est de type lancer deObject
.Résumé:
Object
est tout à fait intéressant: Alors que les deux sont similaires en ce qu'ils permettent d'écrire de manière statique type de code indépendant, l'ancien (génériques) ne pas jeter de presque toutes les informations de type pour atteindre cet objectif. Dans ce sens, les génériques sont un remède àObject
de l'OMI.public static void swap(List<?> list, int i, int j) { swapHelper(list, i, j); } private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); }
,E
est ununiversal type
et?
représente unexistential type
??
dans le typeint height(Tree<?> t)
est pas encore connu à l'intérieur de la fonction, et est encore déterminé par l'appelant parce que c'est l'appelant qui a obtenu de choisir l'arbre pour passer en. Même si les gens appellent ça de l'existentiel type en Java, il n'est pas. Le?
espace réservé peut être utilisés pour mettre en œuvre une forme de existentiels en Java, dans certaines circonstances, mais ce n'est pas l'un d'eux.Ce sont tous de bons exemples, mais je choisis de répondre un peu différemment. Rappel de mathématiques, que ∀x. P(x) signifie "pour tout x, je peux prouver que P(x)". En d'autres termes, c'est un genre de fonction, vous me donnez un x et j'ai une méthode pour le prouver pour vous.
En théorie des types, nous ne parlons pas de preuves, mais de types. Donc, dans cet espace, nous dire "pour tout type de X que vous me donnez, je vais vous donner un type spécifique P". Maintenant, puisque nous ne donnons pas de P beaucoup d'informations à propos de X, outre le fait que c'est un type, P ne peut pas faire grand chose avec elle, mais il y a quelques exemples. P peut créer le type de "toutes les paires du même type":
P<X> = Pair<X, X> = (X, X)
. Ou nous pouvons créer le type d'option:P<X> = Option<X> = X | Nil
, où le Néant est le type des pointeurs null. Nous pouvons faire une liste sur elle:List<X> = (X, List<X>) | Nil
. Remarquez que le dernier est récursive, les valeurs deList<X>
sont soit des couples dont le premier élément est un X et le second élément est uneList<X>
ou bien c'est un pointeur null.Maintenant, en mathématiques ∃x. P(x) signifie "je peux prouver qu'il existe un x tel que P(x) est vraie". Il peut y avoir beaucoup de tels x, mais pour le prouver, un seul suffit. Une autre façon de penser, c'est qu'il doit exister un non-vide ensemble de la preuve et l'épreuve des paires {(x, P(x))}.
Traduit à la théorie des types: Un type dans la famille
∃X.P<X>
est un type de X et d'un type correspondantP<X>
. Notez que si avant nous avons donné X P, (alors que l'on savait tout à propos de X, mais P très peu) que l'inverse est vrai maintenant.P<X>
ne promets pas de donner toutes les informations à propos de X, c'est juste que là il y en a un, et que c'est en effet un type.Comment est-ce utile? Eh bien, P pourrait être un type qui a une façon d'exposer son système interne de type X. Un exemple serait un objet qui cache la représentation interne de son état X. Si nous n'avons aucun moyen de manipuler directement, nous pouvons observer son effet en poussant à P. Il pourrait y avoir de nombreuses implémentations de ce type, mais vous pouvez utiliser tous ces types de n'importe qui, en particulier, a été choisi.
P<X>
au lieu d'unP
(même fonctionnalité et le type de conteneur, disons, mais vous ne savez pas qu'il contientX
)?∀x. P(x)
ne veut rien dire au sujet de la provability deP(x)
, seulement la vérité.Existentielle type est un type opaque.
Penser à un descripteur de fichier sous Unix. Vous savez, il est de type int, de sorte que vous pouvez facilement le forger. Vous pouvez, par exemple, tente de lire à partir de la poignée 43. Si il se trouve que le programme a un fichier ouvert avec cette poignée, vous pourrez lire de lui. Votre code n'a pas à être malveillant, tout simplement bâclée (par exemple, la poignée peut être une variable non initialisée).
Existentielle type est caché de votre programme. Si
fopen
retourné existentielle type, tout ce que vous pouvez faire est de l'utiliser avec certaines fonctions de la bibliothèque qui acceptent ce existentielle type. Par exemple, le pseudo-code de la compilation:L'interface "lu" est déclarée comme suit:
Il existe un type de T tel que:
La variable exfile n'est pas un int, pas un
char*
, pas une structure de Fichier—rien que vous pouvez exprimer dans le système de type. Vous ne pouvez pas déclarer une variable dont le type est inconnu et vous ne pouvez pas convertir, par exemple, un pointeur dans ce type inconnu. La langue ne vous laisse pas.read
est∃T.read(T file, ...)
alors il n'y a rien que vous pouvez passer en tant que premier paramètre. Ce serait le travail est d'avoirfopen
retour le descripteur de fichier et une fonction de lecture de limités par le même existentielle:∃T.(T, read(T file, ...))
Pour répondre directement à votre question:
Avec le type universel, utilise de
T
doit inclure le paramètre de typeX
. Par exempleT<String>
ouT<Integer>
. Pour l'existentiel type utilise desT
ne comprennent pas que le paramètre de type, car il est inconnu ou non pertinentes - il suffit d'utiliserT
(ou en JavaT<?>
).Plus d'informations:
Universel/abstract types et existentielle types de sont une dualité de point de vue entre le consommateur/client de l'objet/fonction et le producteur/mise en œuvre. Quand d'un côté, il voit un type universel à l'autre voit existentielle type.
En Java, vous pouvez définir une classe générique:
MyClass
,T
est universelle parce que vous pouvez remplacer n'importe quel type deT
lorsque vous utilisez la classe et vous devez connaître le type réel de T chaque fois que vous utilisez une instance deMyClass
MyClass
lui-même,T
est existentielle, car il ne sait pas le type réel deT
?
représente l'existentiel type - ainsi, lorsque vous êtes à l'intérieur de la classe,T
est fondamentalement?
. Si vous souhaitez gérer une instance deMyClass
avecT
existentielle, vous pouvez déclarerMyClass<?>
comme dans lesecretMessage()
exemple ci-dessus.Existentielle types sont parfois utilisés pour cacher les détails d'implémentation de quelque chose, comme on le voit ailleurs. Une version Java de ce qui pourrait ressembler à:
C'est un peu difficile à capturer correctement, parce que je suis feignant d'être dans une sorte de langage de programmation fonctionnel, Java n'est pas. Mais le point ici est que vous êtes en capturer quelques-uns sorte de l'état, ainsi qu'une liste de fonctions qui opèrent sur cet état et que vous ne connaissez pas le type réel de l'état partie, mais les fonctions, car ils ont été appariés avec ce type déjà.
Maintenant, en Java, tous les non-final-non-les types primitifs sont en partie existentielle. Cela peut sembler étrange, mais parce qu'une variable déclarée comme
Object
pourrait être une sous-classe deObject
au lieu de cela, vous ne pouvez pas déclarer le type spécifique, seulement "ce type ou d'une sous-classe". Et donc, les objets sont représentés comme un bit d'état en plus d'une liste de fonctions qui opèrent sur cet état - exactement la fonction d'appel est déterminé au moment de l'exécution par la recherche. Ceci est très semblable à l'utilisation de l'existentiel types au-dessus de l'endroit où vous avez existentielle de l'état partie et une fonction qui fonctionne sur cet état.Dans statiquement typé langages de programmation sans sous-typage et de moulages, existentielle, de types permettent de gérer des listes d'différemment les objets de type. Une liste de
T<Int>
ne peut pas contenir unT<Long>
. Cependant, une liste deT<?>
peut contenir toute variation deT
, ce qui permet de mettre beaucoup de différents types de données dans la liste et de les convertir vers un int (ou faire ce que les opérations sont fournies à l'intérieur de la structure de données) sur demande.On peut à peu près toujours de convertir un enregistrement avec une existentiel de type dans un dossier sans l'aide de fermetures. Une fermeture est existentiellement typé, trop, en ce que les variables libres, il est fermé au dessus sont cachés de l'appelant. Donc un langage qui prend en charge de fermetures, mais pas existentielle types peuvent vous permettre de faire des fermetures qui partagent le même état caché que vous avez mis dans la existentiel partie d'un objet.
Semble que je suis venue un peu en retard, mais de toute façon, ce document ajoute un autre point de vue de ce que existentielle types sont, bien que non spécifiquement indépendant de la langue, il devrait être assez facile à comprendre existentielle types: http://www.cs.uu.nl/groups/ST/Projects/ehc/ehc-book.pdf (chapitre 8)
Un type universel qui existe pour toutes les valeurs de type paramètre(s). Existentielle type existe que pour des valeurs du paramètre type(s) qui satisfont les contraintes de l'existentiel type.
Par exemple en Scala, une façon d'exprimer une existentiel type est un type abstrait qui est limitée à certaines supérieure ou inférieure de la limite.
De manière équivalente, une contrainte de type universel est existentielle type comme dans l'exemple suivant.
Toute utilisation du site peut employer le
Interface
parce que tout instanciables des sous-types deExistential
doit définir letype Parameter
qui doit mettre en œuvre lesInterface
.Un cas dégénéré de existentialiste type de Scala est un type abstrait qui n'est jamais mentionné et donc n'a pas besoin d'être défini par un sous-type. C'est effectivement une notation abrégée pour
List[_]
en Scala etList<?>
en Java.Ma réponse a été inspiré par Martin Odersky de proposition d'unifier abstrait et existentielle types. Le accompagnement de la diapositive sida compréhension.
∀x.f(x)
, sont opaques pour recevoir des fonctions Existentielles Types,∃x.f(x)
, sont contraints d'avoir certaines propriétés. Généralement, tous les paramètres sont Existentielle, puisque leur fonction sera de les manipuler directement; cependant, les paramètres génériques peuvent avoir des types qui sont Universels puisque la fonction ne sera pas gérer eux-delà universel de base des opérations telles que l'obtention d'une référence comme dans:∀x.∃array.copy(src:array[x] dst:array[x]){...}
forSome
pour le type de paramètre de quantification existentielle.La recherche sur les types de données abstraits et se cacher de l'information a apporté existentielle types dans les langages de programmation. Fabrication d'un type de données abstrait se cache info à propos de ce type, de sorte qu'un client de ce type ne peut pas en abuser. Disons que vous avez une référence à un objet... certaines langues, vous permettent de jeter qu'un renvoi à une référence d'octets et de faire ce que vous voulez à ce morceau de mémoire. Aux fins de garantir le comportement d'un programme, il est utile pour un langage pour faire valoir ce que de vous d'agir uniquement sur la référence à l'objet via les méthodes le concepteur de l'objet fournit. Vous savez le type existe, mais rien de plus.
J'ai créé ce schéma. Je ne sais pas si c'est de la rigueur. Mais si cela peut aider, je suis content.
Que je comprends que c'est une des mathématiques pour décrire des interfaces/classe abstraite.
Que pour T = ∃X { X; int f(X); }
Pour C# il serait traduit en une générique de type abstrait:
"Existentielle" signifie simplement qu'il y a un type qui obéissent aux règles définies ici.