Je suis nouveau dans la théorie des langages de programmation. Je regardais des conférences en ligne dans lesquelles l'instructeur a affirmé qu'une fonction de type polymorphe forall t: Type, t->t
était l'identité, mais n'a pas expliqué pourquoi. Quelqu'un peut-il m'expliquer pourquoi? Peut-être une preuve de la revendication des premiers principes.
programming-languages
type-theory
abhishek
la source
la source
Réponses:
La première chose à noter est que ce n'est pas nécessairement vrai. Par exemple, selon la langue, une fonction de ce type, en plus d'être la fonction d'identité, pourrait: 1) boucler pour toujours, 2) muter un état, 3) retourner
null
, 4) lever une exception, 5) effectuer des E / S, 6) bifurquer un thread pour faire autre chose, 7) faire descall/cc
manigances, 8) utiliser quelque chose comme JavaObject.hashCode
, 9) utiliser la réflexion pour déterminer si le type est un entier et l'incrémenter si c'est le cas, 10) utiliser la réflexion pour analyser la pile d'appels et faire quelque chose en fonction du contexte dans lequel il est appelé, 11) probablement beaucoup d'autres choses et certainement des combinaisons arbitraires de ce qui précède.Ainsi, la propriété qui y conduit, la paramétricité, est une propriété du langage dans son ensemble et il existe des variations plus fortes et plus faibles de celui-ci. Pour de nombreux calculs formels étudiés en théorie des types, aucun des comportements ci-dessus ne peut se produire. Par exemple, pour le système F / le calcul lambda polymorphe pur, où la paramétricité a été étudiée pour la première fois, aucun des comportements ci-dessus ne peut se produire. Il n'a tout simplement pas d' exceptions, état mutable,
null
,call/cc
, I / O, la réflexion, et il est fortement normalisant il ne peut pas boucler indéfiniment. Comme Gilles l'a mentionné dans un commentaire, le papier Theorems est gratuit!par Phil Wadler est une bonne introduction à ce sujet et ses références iront plus loin dans la théorie, en particulier la technique des relations logiques. Ce lien répertorie également d'autres articles de Wadler sur le thème de la paramétricité.La paramétricité étant une propriété du langage, pour le prouver, il faut d'abord articuler formellement le langage puis un argument relativement compliqué. L'argument informel pour ce cas particulier en supposant que nous sommes dans le calcul lambda polymorphe est que, puisque nous ne savons rien,
t
nous ne pouvons effectuer aucune opération sur l'entrée (par exemple, nous ne pouvons pas l'incrémenter parce que nous ne savons pas si c'est le cas). un nombre) ou créer une valeur de ce type (pour tout ce que nous savonst
=Void
, un type sans aucune valeur). La seule façon de produire une valeur de typet
est de renvoyer celle qui nous est donnée. Aucun autre comportement n'est possible. Une façon de voir cela est d'utiliser une normalisation forte et de montrer qu'il n'y a qu'un seul terme de forme normale de ce type.la source
La preuve de cette affirmation est assez complexe, mais si c'est ce que vous voulez vraiment, vous pouvez consulter l' article original de Reynolds sur le sujet.
L'idée clé est qu'elle vaut pour les fonctions polymorphes paramétriques , où le corps d'une fonction polymorphe est le même pour toutes les instanciations monomorphes de la fonction. Dans un tel système, aucune hypothèse ne peut être faite sur le type d'un paramètre de type polymorphe, et si la seule valeur dans scope a un type générique, il n'y a rien à voir avec cela mais le renvoyer ou le passer à d'autres fonctions que vous '' ve défini, qui à son tour ne peut rien faire mais le retourner ou le passer .. .etc. Donc, à la fin, tout ce que vous pouvez faire est une chaîne de fonctions d'identité avant de retourner le paramètre.
la source
Avec toutes les mises en garde que Derek mentionne, et en ignorant les paradoxes qui résultent de l'utilisation de la théorie des ensembles, permettez-moi d'esquisser une preuve dans l'esprit de Reynolds / Wadler.
Une fonction du type:
est une famille de fonctions indexées par type t .ft t
L'idée est que, pour définir formellement des fonctions polymorphes, nous ne devons pas traiter les types comme des ensembles de valeurs, mais plutôt comme des relations. Les types de base, comme
Int
induire des relations d'égalité - par exemple, deuxInt
valeurs sont liées si elles sont égales. Les fonctions sont liées si elles mappent des valeurs liées à des valeurs liées. Le cas intéressant est celui des fonctions polymorphes. Ils mappent les types associés aux valeurs associées.f
s
t
()
()
t
()
t
((), c)
c
t
()
()
c
c
()
c
t
f
id
Vous pouvez trouver plus de détails sur mon blog .
la source
EDIT: Un commentaire ci-dessus a fourni la pièce manquante. Certaines personnes jouent délibérément avec des langues qui ne sont pas complètes. Je ne me soucie pas explicitement de ces langues. Un langage vraiment non utilisable et complètement utilisable est une chose difficile à concevoir. Le reste de tout cela se développe sur ce qui se passe en essayant d'appliquer ces théorèmes à un langage complet.
Faux!
où l'
is
opérateur compare deux variables pour l'identité de référence. Autrement dit, ils contiennent la même valeur. Pas une valeur équivalente, même valeur. Les fonctionsf
etg
sont équivalentes par définition mais elles ne sont pas les mêmes.Si cette fonction est passée elle-même, elle renvoie autre chose; sinon, il renvoie son entrée. Le quelque chose d'autre a le même type que lui, il peut donc être remplacé. En d'autres termes, ce
f
n'est pas l'identité, carf(f)
revientg
, alors que l'identité reviendraitf
.Pour que le théorème tienne, il doit assumer la capacité ridicule de réduire
Si vous êtes prêt à supposer que vous pouvez supposer que l'inférence de type beaucoup plus facile peut être gérée.
Si nous essayons de restreindre le domaine jusqu'à ce que le théorème se vérifie, nous finissons par devoir le restreindre terriblement.
raise
et nonexit
. Maintenant, nous commençons à être contraints.nil
. Cela commence à devenir problématique. Nous avons manqué de moyens pour faire face à 1 / 0.³L'existence des deux dernières contraintes a paralysé la langue. Bien qu'il soit encore complet sur Turing, le seul moyen d'en tirer un travail général est de simuler une plate-forme interne qui interprète un langage avec des exigences plus lâches.
¹ Si vous pensez que le compilateur peut en déduire celui-ci, essayez celui-ci
² La preuve que le compilateur ne peut pas faire cela dépend de l'aveuglement. Nous pouvons utiliser plusieurs bibliothèques pour nous assurer que le compilateur ne peut pas voir la boucle à la fois. De plus, nous pouvons toujours construire quelque chose où le programme fonctionnerait mais ne pourrait pas être compilé car le compilateur ne peut pas effectuer l'induction dans la mémoire disponible.
³ Quelqu'un pense que vous pouvez avoir ce retour nul sans que les types génériques arbitraires retournent nul. Cela paie une pénalité désagréable pour laquelle je n'ai vu aucun langage efficace qui puisse le payer.
ne doit pas compiler. Le problème fondamental est que l'indexation des tableaux d'exécution ne fonctionne plus.
la source
foil
quantificateur de toute façon?) Ce n'est pas du tout utile.