-Terme.
n.m.
(
ex.progr. déclaration l_terme*)
Terme du
-calcul* construit avec la
-abstraction*,
l´application*
et éventuellement un jeu de constantes.
Terme rationnel.
n.m.
Terme éventuellement infini et composé d'un nombre fini de sous-termes différents.
Un terme fini (par exemple, un terme de l´univers de Herbrand*)
est évidemment rationnel.
Dans la figure suivante,
le terme de gauche,
qui représente essentiellement la liste des entiers,
n'est pas rationnel,
tandis que celui du milieu l'est.
Il représente essentiellement une liste de 0 et de 1 alternés.
Un terme rationnel, même infini, peut être représenté finiment par un graphe dont les sommets sont les sous-termes différents qui le composent et les arcs représentent la relation de sous-terme. Dans la figure ci-dessus, le graphe de droite est la représentation finie du terme du milieu.
L´unification* des termes rationnels est unitaire, décidable
et même relativement aisée [Huet 76].
L'unification mise en
uvre pratiquement dans les systèmes Prolog
est incorrecte à cause de l'omission du
test d´occurrence*.
Remplacer les termes de Prolog par des termes rationnels
est une réponse à ce problème.
-Terme simplement typé.
n.m.
(
ex.progr. déclaration l_terme_st*)
Les
-termes simplement typés sont engendrés par la grammaire suivante :
La grammaire est essentiellement la même que celle des -termes
non-typés,
mais les non-terminaux sont ici décorés d'attributs qui sont des types.
L'accord en type doit être vérifié lors de toute dérivation utilisant cette
grammaire.
Les
et
sont respectivement des identificateurs de constantes et de variables
dont le type est
.
Terzo.
Dernière en date (1997) des implémentations de
Prolog.
C'est un
interpréteur
écrit en Standard
.
C'est un travail commencé par Frank Pfenning et Conal Eliott à l'université
de Carnegie Mellon,
poursuivi par Amy Felty au laboratoire Bell Labs de
,
et finalisé par Philip Wickline à l'université de Pennsylvanie (UPenn).
Test d´occurrence.
n.m.
Test préalable à la formation d'une substitution
par lequel on vérifie que
et
sont compatibles.
En Prolog,
ce test consiste à vérifier que
n'a pas d'occurrence dans
.
Le test d'occurrence s'interprète logiquement en termes
d´élimination de quantificateurs*.
En effet, dans une formule ,
la variable
peut dépendre de
,
mais pas dans une formule
.
Par exemple,
on prouve
en prenant
égal à
,
mais on n'a pas le droit de prouver
de cette façon.
La dépendance peut être indirecte comme dans
où
semble pouvoir dépendre de
,
mais ne le peut pas car il est en fait synonyme de
qui ne peut pas dépendre de
.
Une des manières qu'a l'élimination des quantificateurs de représenter les dépendances entre variables est de remplacer les variables essentiellement universelles par des termes dans lesquelles figurent les variables essentiellement existentielles qui ne peuvent pas en dépendre (c'est-à-dire celles qui sont quantifiées plus à gauche). C'est la skolémisation*. Une autre manière est de remplacer les variables existentielles par des applications de fonctions inconnues aux variables universelles dont elles peuvent dépendre (c'est-à-dire celles qui sont quantifiées plus à gauche). C'est l´antiskolémisation*.
En Prolog,
tout se passe comme si
l'élimination des quantificateurs existentiels etait faite statiquement par
skolémisation en utilisant la première manière.
En Prolog,
les possibilités d'imbrications des quantifications sont plus variées
et les deux manières sont utilisées :
la première,
statiquement, pour ce qui est compatible avec Prolog et la deuxième,
dynamiquement, pour les quantifications de
Prolog qui ne sont
pas réductibles à celles de Prolog.
Dans le dernier cas,
on n'énumère pas vraiment les variables universelles dont une variable
existentielle peut dépendre.
On numérote les variables universelles par ordre d'imbrication de
leurs quantifications
et on ne note que le plus grand des numéros des variables universelles dont elle
peut dépendre.
En Prolog,
le test d'occurrence préalable à la substitution
consiste donc à vérifier que
et aucune variable universelle
interdite dans
n'ont d'occurrence
rigide* dans
,
et à propager dans les variables existentielles de
les variables universelles interdites de
.
Par exemple,
la recherche d'une preuve de
se déroule comme suit,
où on note pour chaque variable existentielle
les variables universelles dont elle peut dépendre.
Ainsi,
dénote que
peut dépendre de
et de
,
et que donc toutes les autres variables universelles sont interdites.
Le but :
Élimination de
Élimination de
Élimination de
Substitution
Échec dû à un test d'occurrence négatif : .
. Reste
.
. Reste
.
. Reste
.
où le test d'occurrence
propage les interdits de
à
. Reste
.
ne peut pas dépendre
de
.
Dans le cas où le terme a une occurrence flexible*,
soit de la variable
,
soit de variables universelles interdites dans
,
il faut que l'une des occurrences flexibles sur le chemin qui y mène soit
remplacée par un terme qui «coupe» le chemin.
Par exemple,
la recherche d'une preuve de
se déroule comme suit.
Ici,
et
, et
et
,
sont des constantes et des variables d'un environnement visible de la formule.
On ne connaît pas les relations qu'entretiennent
,
,
et
.
Le but :
Élimination de
Élimination de
Substitution .
. Reste
.
. Reste
.
.
Dans cet exemple,
le test d'occurrence détecte un chemin flexible vers l'occurrence
interdite .
Ce chemin devra être coupé soit en
,
,
soit en
,
,
soit aux deux applications flexibles.
Comme il n'est pas facile de gérer une disjonction de substitutions,
le système
* suspend la résolution du problème d'unification
comme il est souvent fait en programmation logique avec contraintes.
La résolution du problème sera reprise quand soit
soit
se sera précisée.
En général, les systèmes Prolog n'implémentent pas le test d'occurrence, ou alors il n'est actif qu'à la demande. Cela fait de ces systèmes des démonstrateurs incorrects en toute généralité, mais on observe que le test d'occurrence est le plus souvent inutile, et on connaît assez bien les situations où on peut prouver qu'il l'est [Deransart et al. 91].
Inversement,
on observe qu'en
Prolog la partie du test d'occurrence qui est spécifique
(propagation des variables universelles interdites)
est le plus souvent utile car elle conditionne la bonne formation
des
-abstractions*.
C'est lié à la notion de
variables essentiellement universelles*
qui fait correspondre variables universelles et
-variables*.
La partie commune avec Prolog reste le plus souvent inutile,
mais la formalisation des conditions de son inutilité n'a pas encore été faite.
Une solution pour affranchir Prolog du test d'occurrence est de changer son
domaine de calcul.
Si on remplace les termes de Prolog par des termes rationnels*
comme en Prolog
(
Colmerauer*),
il est assez facile de réaliser une implémentation pour laquelle l'unification
n'est pas plus coûteuse que l'unification de Prolog sans test d'occurrence
[Le Huitouze 88].
On ne peut pas suivre cette approche en
Prolog car cela fait perdre la propriété
de normalisation forte*.
En effet,
on peut très facilement représenter un combinateur de point-fixe avec un
-terme rationnel, même s'il est simplement typé.
Le
-terme rationnel
Y'
suivant est un combinateur de point fixe.
Il est facile de constater que (F ( Y' F)) est égal à
( Y' F) modulo l'égalité des termes rationnels.
Le -terme rationnel Y' se comporte donc comme un
combinateur de point-fixe.
.
Une opération élémentaire
ajoutée au semi-algorithme de Huet* dans son implémentation.
Une paire flexible-rigide* n'est pas passée
directement à la procédure *,
non plus qu'une paire flexible-flexible n'est automatiquement suspendue.
Une telle paire est d'abord passée
à la procédure
,
qui essaie de la résoudre en appliquant des heuristiques «simples».
Si aucune heuristique ne s'applique,
la paire est alors passée à
ou suspendue.
Les heuristiques reconnaissent des paires
(dites «paires triviales»)
sous des formes diverses.
Si une telle paire est reconnue et si la variable
n'apparaît pas
dans le terme
,
alors la substitution
est un unificateur de cette
paire.
Le test d´occurrence*
est plus compliqué que son homologue du premier
ordre car les occurrences de
dans
ne causent pas toutes un échec.
Il se complique encore avec l'introduction des quantifications.
Une bonne procédure doit reconnaître une paire triviale sous
les formes suivantes.
Type abstrait.
n.m.
Les quantifications de
Prolog fournissent un support naturel pour
définir des types abstraits et leurs méthodes [Miller 89a].
L'idée est d'exploiter l'identité suivante :
Cette identité montre qu'en
Prolog on peut aussi avoir des quantifications
existentielles au niveau des clauses,
mais à la condition qu'elles soient complètement extérieures
à une clause,
car alors on pourra les remplacer par des quantifications
universelles au niveau d'un but.
D'après les règles d'élimination des quantificateurs,
ces quantifications existentielles seront éliminées en remplaçant
les variables quantifiées par de nouvelles constantes.
Par définition,
ces constantes ne peuvent pas être connues du programmeur.
On peut donc les considérer comme des constructeurs cachés d'un type
abstrait dont les méthodes sont les prédicats laissés visibles.
Par exemple,
les déclarations et définitions suivantes,
kind pile type -> type .
type est_vide (pile A) -> o .
type sommet A -> (pile A) -> (pile A) .
type hauteur (pile A) -> int -> o .
sigma vide
est_vide vide ,
pi S
hauteur vide zéro ,
pi S(sigma empiler
(
(pi P
( sommet S P (empiler S P) )) ,
(pi P
(pi H
( hauteur (empiler S P) (succ H) :- hauteur P H ))) )) .
introduisent un type abstrait pile, dont les constructeurs vide et empiler sont cachés, mais dont les méthodes est_vide, sommet et hauteur sont visibles.
Type inductif.
n.m.
Se dit du type d'une structure de données dont les
constructeurs n'ont des sous-termes de ce type dans leurs arguments
qu'en des occurrences
positives [Böhm et Berarducci 85, Pierce et al. 89].
Il faut se rappeler que selon la
correspondance de Curry-Howard*
la flèche des types simples est l'analogue de
l'implication du calcul des propositions.
Comme elle,
elle introduit une notion d'occurrences
négatives* et positives*
selon la définition suivante :
Par exemple,
pour un type égal à
,
on a
et
.
En Prolog,
la notion de type inductif permet de décider sans avoir recours
au raisonnement opérationnel s'il faut utiliser l'implication dans les buts
dans une induction structurelle
(
induction structurelle en
Prolog*).
Si le type d'une structure de données est inductif
alors on en déduit facilement une fonction d'induction structurelle sur ses
constructeurs [Böhm et Berarducci 85, Pierce et al. 89],
sans utiliser l'implication.
En revanche,
si le type n'est pas inductif,
l'induction structurelle standard ne suffit pas.
Dans tous les cas
(inductif ou non),
la quantification universelle dans les buts sera nécessaire pour les
arguments d'ordre supérieur.
De plus,
dans le cas non-inductif,
l'implication dans les buts sera aussi nécessaire pour les occurrences
négatives.
Les occurrences négatives correspondent aux types des variables universelles utilisées pour interpréter les abstractions. Donc, si le type d'intérêt a une occurrence négative, il faudra utiliser l'implication pour augmenter le programme afin de prendre en compte la constante universelle utilisée pour interpréter la quantification universelle.
L'exemple d'induction structurelle sur les formules
( prédicat norm_nég*)
n'utilise pas l'implication.
C'est parce que le type
formule* est inductif.
Ses constructeurs sont soit des connecteurs (et, etc.),
soit des quantificateurs (qqsoit, etc.).
Le type de tous les arguments de et est formule.
Le type de l'unique argument de qqsoit est
individu
formule.
Or, on a
neg(formule)=
et
neg(individu
formule)=
individu
.
Aucun ne contient formule,
donc le type formule est inductif.
Cette exemple montre aussi que les quantifications du métalangage sont utilisées pour interpréter la structure des formules objet, indépendamment de leur sémantique. Ici, la même quantification universelle est utilisée pour traiter les quantifications existentielles et les quantifications universelles des formules objet.
Au contraire,
tous les exemples présentés sur les -termes objets
(
prédicats bien_typé* et de_bruijn*)
utilisent l'implication.
Montrons que le type l_terme* n'est pas inductif.
Ses constructeurs sont app et abs,
et le type de abs est
(l_terme
l_terme)
l_terme.
Le type de l'unique argument de abs est
donc l_terme
l_terme.
Or
neg(l_terme
l_terme)=
l_terme
.
Le type l_terme a une occurrence négative dans le type
d'un argument d'un de ces constructeurs ;
il n'est donc pas inductif.
Type oublié.
n.m.
Instance d'une variable de type qui est oubliée par un
type oublieur*.
Avec les types des variables logiques,
ce sont les seuls types qu'il est nécessaire de représenter à l'exécution
[Brisset et Ridoux 92b, Brisset et Ridoux 94].
Type oublieur.
n.m.
Type qui viole la condition de transparence*.
Par exemple,
tous les types de prédicats polymorphiques sont oublieurs.
Type paramétrique.
n.m.
[Louvet et Ridoux 96, Louvet 96]
La discipline de typage du
-calcul* polymorphique du second
ordre,
(
cube de Barendregt*),
peut être transposée à la programmation logique.
Cela permet de s'affranchir de la
condition de tête*
et de décrire convenablement la représentation des types à l'exécution.
Le système obtenu en appliquant cette discipline à
Prolog est appelé
Prolog
(voir la section
«Typage polymorphe paramétrique»*).
Type produit.
n.m.
Un type produit
est celui de «fonctions»
dont le type du résultat,
,
peut dépendre de la valeur du paramètre,
.
Un type flèche
peut être considéré comme une abréviation
d'un type produit lorsque le type du résultat de dépend pas de la valeur du
paramètre.
Pourquoi ce nom de «type produit» ?
L'explication pour les fonctions d'un type
fini dans un autre type
fini
est assez simple.
Elles sont complètement déterminées par leurs graphes,
qui sont des vecteurs de
.
On peut donc noter le type de ces fonctions par
,
et cette notation s'étend sans difficulté au cas où
n'est pas fini,
et avec un peu plus de difficulté au cas où
ne l'est pas.
Considérons maintenant des fonctions d'un type
fini
dans des types finis
déterminés pour chaque élément de
.
Elles sont encore complètement déterminées par leurs graphes,
qui sont des vecteurs de
,
qu'il est plus commode de noter
.
À nouveau cette notation s'étend au cas infini.
Si tous les
sont les mêmes,
on retrouve naturellement la notation
.
Si les sont des types,
alors le type produit modélise la notion de polymorphisme paramétrique
(voir la section
«Typage polymorphe paramétrique»*).
Cette faculté correspond à la face «non prédicative»
(aussi notée
)
du cube de Barendregt*
(
figure 1*).
Si les
sont des termes,
alors le type produit modélise la notion de type dépendant.
Cette faculté correspond à la face des «preuves»
(aussi notée
).
Type simple.
n.m.
Les types
simples [Church 40, Barendregt 91]
sont engendrés par la grammaire suivante :
Les sont des identificateurs de
constructeurs de type d'arité
.
La deuxième règle est la règle de formation des types de fonctions ;
le type
peut être interprété
comme celui des fonctions de
vers
.
On admet que la flèche
est associative à droite,
ce qui rend certaines parenthèses inutiles :
par exemple,
o
o
o dénote le même type que
(o
(o
o)).
La notation concrète de
dans les programmes
est ->.
La représentation en Prolog des types simples de niveau objet est la suivante :
kind type_simple type .
type flèche type_simple -> type_simple -> type_simple .
type base type_simple .
Les règles de bon typage au niveau objet sont les suivantes :
type bien_typé l_terme -> type_simple -> o .
bien_typé (app E F) Tau :- bien_typé E (flèche Sigma Tau) , bien_typé F Sigma .
bien_typé (abs E) (flèche Sigma Tau) :-
pi x( bien_typé x Sigma => bien_typé (E x) Tau ) .